1.结构体和C语言类似,system verilog定义的package,也是一个sv文件,需要被工程当作文件一样添加。如果是inclue的文件,要在工程中设置搜索路径,否则在引用时要用相对工程启动文件的路径。在其他sv文件中,"import dma_define::*;"在最前面添加即可。
package dma_define;
//M9K max 256*36 72bit
//130bit
typedef struct packed {
logic sop ;
logic eop ;
logic [63:0] data;
logic [ 7:0] ptr ;
logic [15:0] len ;
logic [39:0] addr;
} DMA_ST;
//66bit
//typedef struct packed {
// logic sop ;
// logic eop ;
// logic [ 7:0] ptr ;
// logic [15:0] len ;
// logic [39:0] addr;
//} BD_ST;
endpackage
import dma_define::*;
module cyclone_dma_if (
input clk ,
input rst ,
//pcie avmm
output reg [23:0] txs_address =0, // txs.address
output reg txs_chipselect =0, // .chipselect
output wire [ 7:0] txs_byteenable , // .byteenable
input [63:0] txs_readdata , // .readdata
output reg [63:0] txs_writedata =0, // .writedata
output reg txs_read =0, // .read
output reg txs_write =0, // .write
output reg [ 6:0] txs_burstcount =0, // .burstcount
input txs_readdatavalid, // .readdatavalid
input txs_waitrequest , // .waitrequest
//pcie write 写bd finish 和写数据到cpu
output pcie_wcmdq_ren ,
input DMA_ST pcie_wcmdq_dat ,
input pcie_wcmdq_empty ,
//pcie read 下行
output wire pcie_rcmdq_ren ,
input DMA_ST pcie_rcmdq_dat ,
input pcie_rcmdq_empty ,
//pcie 返回数据
output reg pcie_recvd_wen ,
output DMA_ST pcie_recvd_dat , //下行无法反压
output wire [ 4:0][31:0] debug
);
2.三段式状态机,用枚举的方法定义状态,方便仿真和代码阅读。
typedef enum bit [2:0] {
FSM_IDLE,
FSM_READ,
FSM_WAIT_READ,
FSM_WRITE,
FSM_WAIT_WRITE,
FMS_NOP
}FSM_STATE;
FSM_STATE fsm_curr,fsm_next;
always@(posedge clk)begin
if(rst)
fsm_curr <= FSM_IDLE;
else
fsm_curr <= fsm_next;
end
always@(*)begin
case (fsm_curr)
FSM_IDLE:
FSM_READ:
FSM_WAIT_READ:
FSM_WRITE:
FSM_WAIT_WRITE:
FMS_NOP:
default
endcase
end
always@(posedge clk)begin
case(fsm_curr)
default:;
endcase
end
3.interface。使用上和结构体的最大区别就是它有modport,可以区分方向。结构体一定是单向的,一般结构体都是和wen afull,或者ren和empty作为fifo的前后传递信号用。interface也是定义在一个sv文件中,一个sv可以用interface和endinterface定义多个接口。当成工程文件引用,不必在其他模块中声明就可以使用该接口。
interface AVALON_MM #(
parameter DATA_W = 32,
parameter ADDR_W = 8
)();
logic write;
logic [DATA_W-1:0] writedata;
logic read;
logic [ADDR_W-1:0] address;
logic [DATA_W-1:0] readdata;
logic readdatavalid;
logic waitrequest;
modport Slave(input write,writedata,read,address,output readdata,readdatavalid,waitrequest);
modport Master(output write,writedata,read,address,input readdata,readdatavalid,waitrequest);
endinterface
使用时要注意方向。最经典的应用就是一个模块内声明,类似于wire,一个模块输入,一个输出。可以大大节约代码行数。interface就类似一个模块,还是用generate的语法去生成和索引,很是灵活。
AVALON_MM#(32,8) dma_csr() ;
AVALON_MM#(32,8) gtx_csr() ;
wire [ 5:0][31:0] prase_rd_debug ;
wire [ 2:0][31:0] sch_wr_debug ;
wire [ 4:0][31:0] dma_if_debug ;
pcie_dma inst_pcie_dma (
.clk (pcie_clk ),
.rst (dma_rst ), //input
.txs_address (txs_address ),
.txs_chipselect (txs_chipselect ),
.txs_byteenable (txs_byteenable ),
.txs_readdata (txs_readdata ),
.txs_writedata (txs_writedata ),
.txs_read (txs_read ),
.txs_write (txs_write ),
.txs_burstcount (txs_burstcount ),
.txs_readdatavalid (txs_readdatavalid ),
.txs_waitrequest (txs_waitrequest ),
.igress_ren (igress_ren ), //pcie_clk
.igress_rdata (igress_rdata ),
.igress_empty (igress_empty ),
.ig_bd_usedw (ig_bd_usedw ),
.eg_rspda_wen (eg_rspda_wen ), //pcie_clk
.eg_rspda_dat (eg_rspda_dat ),
.eg_rspda_afull (eg_rspda_afull ),
.prase_rd_debug (prase_rd_debug ),
.sch_wr_debug (sch_wr_debug ),
.dma_if_debug (dma_if_debug ),
.avmm (dma_csr ) //slave 8bit symbol
);
gtx inst_gtx(
.clk (clk ),
.gtx_csr (gtx_csr.Slave )
);
avmm_conv inst_avmm_conv (
.pcie_clk (pcie_clk ), //input
.pcie_rstn (pcie_rstn ), //input
.i_bar_address (bar0_address ), //input [19:0]
.i_bar_byteenable (bar0_byteenable ), //input [03:0]
.i_bar_read (bar0_read ), //input
.i_bar_readdata (bar0_readdata ), //output reg [31:0]
.i_bar_readdatavalid (bar0_readdatavalid ), //output reg
.i_bar_write (bar0_write ), //input
.i_bar_writedata (bar0_writedata ), //input [31:0]
.i_bar_waitrequest (bar0_waitrequest ), //output wire
.cra_chipselect (cra_chipselect ), //output reg
.cra_address (cra_address ), //output reg [11:0]
.cra_byteenable (cra_byteenable ), //output wire [ 3:0]
.cra_read (cra_read ), //output reg
.cra_readdata (cra_readdata ), //input [31:0]
.cra_write (cra_write ), //output reg
.cra_writedata (cra_writedata ), //output reg [31:0]
.cra_waitrequest (cra_waitrequest ), //input
.gtx_csr (gtx_csr.Master ),
.dma_write (dma_csr.write ),
.dma_writedata (dma_csr.writedata ),
.dma_read (dma_csr.read ),
.dma_address (dma_csr.address ),
.dma_readdata (dma_csr.readdata ),
.dma_readdatavalid (dma_csr.readdatavalid),
.dma_waitrequest (dma_csr.waitrequest ) //这里故意写成这样,方便理解
);
system verilog的标准上都写明了interface等语法的用法,更多的用法是应用在仿真中。但仅仅学习了一些最基本的,也可以将代码写得更加简洁。
4.$fopen函数。
我们可以用以下代码创建一个文件句柄,在仿真中把打印内容写到txt文本中。
integer fw;
initial begin
fw = $fopen("run.txt","w+");
end
always@(posedge clk)begin
$fwrite(fw,"%t : test[%d] is running!\n",$time,0);
end
可以看到$fwrite最末尾添加了换行符,也是和display的区别,后者会自己加换行符。
但当我们有N个重复例化的模块时,我们相应地也要例化N个log文件,最笨的方法是例化N次,给fopen传递不同的run_0.txt,run_1.txt等。可以用$sformatf拼接字符串,用generate来创建和写入。
integer fw[8];
string fw_str[8];
reg [7:0] test_id = 8'd0;
genvar i;
generate
for(i=0;i<8;i=i+1)begin
initial begin
fw_str[i] = $sformatf("run_%0d.txt",i);
fw [i] = $fopen(fw_str[i],"w+");
end
always@(posedge clk)begin
$fwrite(fw[i],"%t : test[%d] is running!\n",$time,test_id[i]);
end
end
endgenerate