用于发送串行字节的简单 VHDL 测试平台程序?

Simple VHDL testbench procedure for sending serial bytes?

我正尝试在我的设计中删除 bitbashing,并使用程序从 DUT 外部发送测试信号。序列化消息的格式是起始位为“0”,字节以 MSB 在前,停止位为“1”。线路空闲在“1”。我想我在使用要在过程和主进程之间传递的数据类型时遇到问题。

这是我的程序(波特率是串行时钟周期的时间常数):

  procedure send_midi_byte (
    signal byte_in  : in  std_logic_vector;
    signal midi_out : out std_logic) is
  begin
    midi_out <= '0';
    wait for baudrate;
    for i in 7 to 0 loop
      midi_out <= byte_in(i);
      wait for baudrate;
    end loop;
    midi_out <= '1';
    wait for baudrate;
  end send_midi_byte;

下面是我调用它发送几个字节的方式(byte_slv 是一个 8 元素 std_logic_vector):

    byte_slv <= x"90";
    send_midi_byte(byte_slv, midi_in_int);

我尝试了几种不同的方法,这是唯一不会出错的方法,但当然它不会起作用,因为过程中的非阻塞分配意味着我的串行信号将只是 ' 1'表示波特率中指定的时间长度。

如何正确编写此程序?

问题 你有两个错误。首先,Brian 指出,您的范围是错误的。表达范围的一种简单方法是使用“范围”。这显示在下面的代码中。

此外,您想将 byte_in 参数的 class 更改为常量,如下面的代码所示。

解决方案

  procedure send_midi_byte (
    constant byte_in  : in  std_logic_vector;
    signal   midi_out : out std_logic
  ) is
  begin
    midi_out <= '0';
    wait for baudrate;
    for i in byte_in'range loop
      midi_out <= byte_in(i);
      wait for baudrate;
    end loop;
    midi_out <= '1';
    wait for baudrate;
  end send_midi_byte;

那么为什么要常量呢?常量意味着参数在子程序调用的生命周期内是静态的。常量参数允许任何产生值的东西(文字、表达式,甚至信号或变量名),例如:

send_midi_byte(x"90", midi_in_int);
for i in 0 to 10 loop
  send_midi_byte(x"90" + i, midi_in_int);
end loop;

您已经熟悉这一点,因为所有语言运算符(作为函数实现)都有常量 classed 参数(这是默认设置,在标准包中通常将其关闭)。

参数指南
对于输入:
当对象需要更新时使用信号 - 例如使用'事件,或在等待语句中使用对象。通常这意味着对象来自体系结构(例如 DUT)。
当您需要传递文字值时,使用常量

对于输出:
将值传递给架构(例如 DUT)时使用信号
将值传回本地进程时使用变量

为什么参数要是常量
在您的示例中,它将使用信号,但是,使用模型将始终是:1) 分配给信号,然后 2) 调用子程序:

byte_slv <= x"90";
send_midi_byte(byte_slv, midi_in_int);

这可能会使您烦恼,也可能不会成为问题。对于其他过程,信号参数的更新可能确实以太迟的增量循环结束。这也可以很容易地在子程序中解决。

OTOH,一旦您想从另一个程序调用您的 send_midi_byte 程序,它就会变得更加困难或根本不起作用。在下面的过程中,我们尝试通过向 midi 发送递增字节来进行测试:

procedure send_inc_midi_bytes (
  constant first_byte_in : in std_logic_vector;
  constant number_bytes  : in integer ;
  signal midi_out  : std_logic
) is 
   -- signal byte_in : std_logic_vector(7 downto 0); -- want to do this
begin
  for i in 0 to number_bytes - 1 loop
    byte_in <= byte_in + i ; 
    send_midi_byte(byte_in, midi_out);
  end loop ;
end procedure send_inc_midi_bytes ; 

我知道固定范围可以解决您的确切问题,但有一天,您可能希望做更多 - 到那时,您会希望有人建议您使用常量而不是信号。

除了 Jim 的建议,修复循环范围并在尝试任何其他操作之前重试。 7 to 0 是空范围; 0 to 77 downto 0 都不是。

非阻塞(关于赋值)在 VHDL 中是一个没有意义的术语,但在一个过程中的循环内的信号赋值绝对没问题。这是一项推迟的作业,计划在 wait for baudrate 开始时进行。那当然是等待指定的时间,完成后开始下一次循环迭代。

I have tried a few different methods and this is the only one that doesn't give an error, but of course it won't work because of the nonblocking assignments in the procedure meaning my serial signal will just be '1' for the length of time specified in baudrate.

你这里断言的前提是不正确的。过程调用失败的原因是循环参数规范提供了一个空值范围(先计算循环参数,循环语句执行0次或多次)。

How can I write this procedure correctly?

程序 send_midi_byte:

中包含修复程序的最小、完整且可验证的示例
library ieee;
use ieee.std_logic_1164.all;

entity cmoc_cmoc is
end entity;

architecture mcve of cmoc_cmoc is
    constant baudrate:  time := 26.925 us;  -- 38.400 baud
    procedure send_midi_byte (
        signal byte_in:   in  std_logic_vector;
        signal midi_out:  out std_logic
    ) is
        alias in_byte: std_logic_vector (7 downto 0) is byte_in;  -- ADDED
    begin
        midi_out <= '0';
        wait for baudrate;
        for i in in_byte'range loop        -- WAS 7 to 0
            midi_out <= in_byte(i); -- WAS byte_in(i);
            wait for baudrate;
        end loop;
        midi_out <= '1';
        wait for baudrate;
    end procedure send_midi_byte;
    signal some_byte:   std_logic_vector (7 downto 0) := x"41";
    signal midi_out:    std_logic := '1';
    type baud is (IDLE,START, BD0, BD1, BD2, BD3, BD4, BD5, BD6, BD7, STOP);
    signal baud_cnt:    baud;
begin
PROCEDURE_CALL:
    process 
    begin
        wait for baudrate;  -- SHOW IDLE on midi_out;
        send_midi_byte(some_byte, midi_out);  -- added second parameter
        wait;
    end process;
BAUD_CTR:
    process
    begin
        if baud_cnt = IDLE then
            wait until midi_out = '0';
        end if;
        loop
            baud_cnt <= baud'RIGHTOF(baud_cnt);
            wait for 0 ns;
            report "baud(" & baud'image(baud_cnt) &
                   ") midi_out = "  & std_ulogic'image(midi_out);
            wait for baudrate;
            if baud_cnt = STOP then
                baud_cnt <= IDLE;
                exit;
            end if;
        end loop;
        wait;
    end process;
end architecture;

请注意,byte_in 的 class 未在过程声明中提供。对于模式 in 的子程序参数,默认 class 是 constant.

已保留 MSB 第一顺序以匹配您的问题文本。 (UART 首先传输 LSB)。

BAUD_CTR过程是为了演示传输波特率的点缀。

对于输入字节为左侧 MSB 并首先传输 LSB 的 UART,可以反转别名范围或循环参数可以使用`REVERSE_RANGE.

报告报表:

ghdl -r cmoc_cmoc --wave=cmoc_cmoc.ghw
cmoc_cmoc.vhdl:45:13:@26925ns:(report note): baud(start) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@53850ns:(report note): baud(bd0) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@80775ns:(report note): baud(bd1) midi_out = '1'
cmoc_cmoc.vhdl:45:13:@107700ns:(report note): baud(bd2) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@134625ns:(report note): baud(bd3) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@161550ns:(report note): baud(bd4) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@188475ns:(report note): baud(bd5) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@215400ns:(report note): baud(bd6) midi_out = '0'
cmoc_cmoc.vhdl:45:13:@242325ns:(report note): baud(bd7) midi_out = '1'
cmoc_cmoc.vhdl:45:13:@269250ns:(report note): baud(stop) midi_out = '1'

波形显示baud_cnt