作者:Jacky Gao,AMD工程师;来源:AMD开发者社区
前言
在之前的文章中,我们介绍了如何使用SystemC和VIP做C/C++/RTL的联合仿真,以及如何用SystemC实现AXI4-Lite Master做C/C++/RTLRTL的联合仿真。
在本文中,我们将继续探讨,介绍如何用SystemC实现完整的AXI4协议,以实现DMA的测试。
文中所涉及的所有代码均在Vivado和Modelsim/Questasim上做了验证
简介
在SystemC中实例化一个RTL的实体
在SystemC中实例化一个RTL的实体很直观,你只需要为其RTL实体手写一个对应的SystemC的外部模块,之后这个模块就可以在SystemC的环境被被其它类调用。
下面将举例说明如果在SystemC中构建一个VHDL的实体
示例 VHDL的设计
entity counter is port( clk : in std_logic; reset: in std_logic; count: std_logic_vector(7 downto 0) ); end; architecture rtl of counter is … end rtl;
对应的SystemC模块
class counter : public sc_foreign_module { public: sc_in<sc_logic> clk; sc_in<sc_logic> reset; sc_out<sc_lv<8> > count; counter(sc_module_name nm) : sc_foreign_module(nm, "work.counter"), clk("clk"), reset("reset"), count("count") { } };
实际的应用中,可以在编译完RTL模块之后,使用Modelsim/Qustasim提供的scgenmod工具自动生成对应的SystemC模块,具体使用办法请参考Modelsim手册。
用SystemC设计BFMs
我们参考ARM AMBA AXI Protocol Spec文档,使用SystemC实现完整的AXI4协议如下:
在SystemC中实现AXI4 Full的读操作,
uint32_t cmd_count = 0; bool cmd_done = false; uint32_t rsp_count = 0; bool rsp_done = false; bool wait_state = false; uint32_t wait_count = 0; uint32_t start_addr = addr; uint32_t num_transfers = size / 2; uint32_t index = 0; while (!cmd_done) { sc_core::wait(aclk_i.posedge_event()); num_transfers = (size / 2 - cmd_count) > 256 ? 256 : (size / 2 - cmd_count); araddr = start_addr + cmd_count * 8; arlen = num_transfers - 1; arvalid = SC_LOGIC_1; rready = SC_LOGIC_0; arburst = INCR; arsize = WL64; while (1) { sc_core::wait(aclk_i.posedge_event()); if (arready.read() == SC_LOGIC_1) break; } arvalid = SC_LOGIC_0; rready = SC_LOGIC_1; while (1) { sc_core::wait(aclk_i.posedge_event()); if (rvalid.read() == SC_LOGIC_1) { *data = rdata.read().to_uint64(); data++; index += 2; uint32_t rsp = rresp.read().to_uint(); rc = checkAxiResponse(rsp); if (rlast.read() == SC_LOGIC_1) { cmd_count += num_transfers; if (cmd_count == size / 2) cmd_done = true; break; } } } }
在SystemC中实现AXI4 Full的写操作可以如下,
uint32_t cmd_count = 0; bool cmd_done = false; uint32_t start_addr = addr; uint32_t num_transfers = size / 2; uint32_t wait_count = 0; uint32_t index = 0; while (!cmd_done) { sc_core::wait(aclk_i.posedge_event()); num_transfers = (size / 2 - cmd_count) > 256 ? 256 : (size / 2 - cmd_count); awaddr = start_addr + cmd_count * 8; awlen = num_transfers - 1; awvalid = SC_LOGIC_1; wdata = *data; wstrb = 0xFF; wvalid = SC_LOGIC_0; wlast = SC_LOGIC_0; awburst = INCR; awsize = WL64; while (1) { sc_core::wait(aclk_i.posedge_event()); if (wready.read() == SC_LOGIC_1) break; } for (uint32_t i = 0; i < num_transfers; i++) { awaddr = start_addr + cmd_count * 8; awlen = num_transfers - 1; awvalid = SC_LOGIC_0; wdata = *(data + i); wstrb = 0xFF; wvalid = SC_LOGIC_1; wlast = (i == (num_transfers - 1)) ? SC_LOGIC_1 : SC_LOGIC_0; while (1) { sc_core::wait(aclk_i.posedge_event()); if (wready.read() == SC_LOGIC_1) break; } index++; cmd_count++; if (cmd_count == size / 2) cmd_done = true; } wvalid = SC_LOGIC_0; wlast = SC_LOGIC_0; while (1) { if (bvalid.read() == SC_LOGIC_1) { rc = true; break; } sc_core::wait(aclk_i.posedge_event()); wait_count++; if (wait_count == 10) break; } }
示例工程
打开Vivado,打开附件中的工程,查看DUT设计。
打开Modelsim/Questasim,进入到sim/questa文件中
执行do test.do
测试结果
以下是Questasim的仿真结果,可以查看波形文件看到SystemC如何实现AXI4的Burst传输。