在之前的文章中,我们介绍了如何使用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
clk; sc_in
reset; sc_out
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手册。
我们参考ARM AMBA AXI Protocol Spec文档,使用SystemC实现完整的AXI4协议如下:
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) {
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;
}
}
}
}
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传输。