该设计我们称之为多通道数据整形器(MCDF,multi-channel data formatter),它可以将上行(uplink)多个通道数据经过内部的FIFO,最终以数据包(data packet)的形式送出。
由于上行数据和下行数据的接口协议不同,我们也将在后面的接口描述和时序部分进一步讲解。此外,多通道数据整形器也有寄存器的读写接口,可以支持更多的控制功能。
当valid为高时,表示要写入数据。如果该时钟周期ready为高,则表示已经将数据写入;如果该时钟周期ready为低,则需要等到ready为高的时钟周期才可以将数据写入。
在控制寄存器接口上,需要在每一个时钟解析cmd。当cmd为写指令时,需要把数据cmd_data_in写入到cmd_addr对应的寄存器中;当cmd为读指令时,即需要从cmd_addr对应的寄存器中读取数据,并在下一个周期,将数据驱动至cmd_data_out接口。
同通道1控制寄存器描述。
同通道1控制寄存器描述。
同通道1状态寄存器描述。
同通道1状态寄存器描述。
要注意设计文件的路径名不能有中文名
2. 编译设计文件
3. 仿真tb文件
在work库里面找到编译好的tb文件,右键进行仿真
4. 添加信号波形
选中dut中所有in、out端口信号,右键add ware
命令窗口执行“run 1us”
分析波形
当ch0_data_i要发送数据的时候,波形由x变成了00c00000,同时ch0_valid_i信号由x变成了1,代表发送数据有效,ch0_ready_o也拉高,代表已经准备好接受数据。
当把数据送进入之后,ch0_margin_o信号的值由20变成1f,是因为当送入一个数据之后fifo的余量减少了一个。
当mcdt_data_o把数据送出的时候,fifo余量就会加1,从1f又变成了20。
整体来看显示通道1发送数据,再是通道2发送数据,最后是通道3发送数据。
分析设计文件代码
时钟5ns一翻转,也就是时钟周期是10ns
// clock generation
initial begin
clk <= 0;
forever begin
#5 clk <= !clk;
end
end
复位信号在仿真开始10ns时置0,等待10个时钟周期后,也就是在10ns+100ns=110ns时置1。
// reset trigger
initial begin
#10 rstn <= 0;
repeat(10) @(posedge clk);
rstn <= 1;
end
然后开始发送数据,通道1先发送。等待复位信号拉高时,再等待5个时钟周期,即第6个周期的上升沿才开始发送数据。也就是说110ns+5*10ns+5ns=165ns时发送数据。此时ch0_valid_i置1,同时把数据写入进去。
// data test
initial begin
@(posedge rstn);
repeat(5) @(posedge clk);
// channel 0 test
chnl_write(0, 'h00C0_0000);
chnl_write(0, 'h00C0_0001);
chnl_write(0, 'h00C0_0002);
chnl_write(0, 'h00C0_0003);
chnl_write(0, 'h00C0_0004);
chnl_write(0, 'h00C0_0005);
chnl_write(0, 'h00C0_0006);
chnl_write(0, 'h00C0_0007);
chnl_write(0, 'h00C0_0008);
chnl_write(0, 'h00C0_0009);
// channel write task
task chnl_write(input reg[1:0] id, input reg[31:0] data);
case(id)
0: begin
@(posedge clk);
ch0_valid <= 1;
ch0_data <= data;
@(posedge clk);
ch0_valid <= 0;
ch0_data <= 0;
end
1: begin
@(posedge clk);
ch1_valid <= 1;
ch1_data <= data;
@(posedge clk);
ch1_valid <= 0;
ch1_data <= 0;
end
2: begin
@(posedge clk);
ch2_valid <= 1;
ch2_data <= data;
@(posedge clk);
ch2_valid <= 0;
ch2_data <= 0;
end
default: $error("channel id %0d is invalid", id);
endcase
endtask
在下一拍的时候,会将ch0_valid_i置0,所以从波形上看,相邻的数据之间valid拉低,有一个无效的数据,处于idle的状态。
当ch0_valid被赋值以后,接下来就会对dut待测设计的输入做操作了。
mcdt dut(
.clk_i(clk)
,.rstn_i(rstn)
,.ch0_data_i(ch0_data)
,.ch0_valid_i(ch0_valid)
,.ch0_ready_o(ch0_ready)
,.ch0_margin_o(ch0_margin)
,.ch1_data_i(ch1_data)
,.ch1_valid_i(ch1_valid)
,.ch1_ready_o(ch1_ready)
,.ch1_margin_o(ch1_margin)
,.ch2_data_i(ch2_data)
,.ch2_valid_i(ch2_valid)
,.ch2_ready_o(ch2_ready)
,.ch2_margin_o(ch2_margin)
,.mcdt_data_o(mcdt_data)
,.mcdt_val_o(mcdt_val)
,.mcdt_id_o(mcdt_id)
);