Skip to content

Commit 1f99de5

Browse files
committed
add examples for APB
1 parent 00a378f commit 1f99de5

File tree

3 files changed

+339
-0
lines changed

3 files changed

+339
-0
lines changed

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
if(NOT USE_CWR_SYSTEMC)
22
add_subdirectory(ace-axi)
33
add_subdirectory(ace-ace)
4+
add_subdirectory(apb_bfm)
45
add_subdirectory(axi-axi)
56
add_subdirectory(axi4_tlm-pin-tlm)
67
add_subdirectory(axi4lite_tlm-pin-tlm)

examples/apb_bfm/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
project (apb_bfm)
2+
3+
add_executable(${PROJECT_NAME} sc_main.cpp)
4+
target_link_libraries (${PROJECT_NAME} PUBLIC scc)
5+
target_link_libraries (${PROJECT_NAME} LINK_PUBLIC ${CMAKE_THREAD_LIBS_INIT})
6+
target_link_libraries (${PROJECT_NAME} LINK_PUBLIC ${CMAKE_DL_LIBS})
7+
if(APPLE)
8+
set_target_properties (${PROJECT_NAME} PROPERTIES LINK_FLAGS
9+
-Wl,-U,_sc_main,-U,___sanitizer_start_switch_fiber,-U,___sanitizer_finish_switch_fiber)
10+
endif()

examples/apb_bfm/sc_main.cpp

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,328 @@
1+
#include <cci_configuration>
2+
#include <fstream>
3+
#include <interfaces/apb/pin/initiator.h>
4+
#include <interfaces/apb/pin/target.h>
5+
#include <iomanip>
6+
#include <scc/configurable_tracer.h>
7+
#include <scc/configurer.h>
8+
#include <scc/report.h>
9+
#include <scc/traceable.h>
10+
#include <scc/tracer.h>
11+
#include <tlm/scc/initiator_mixin.h>
12+
#include <tlm/scc/target_mixin.h>
13+
14+
using namespace sc_core;
15+
using namespace scc;
16+
17+
class testbench : public sc_module, public scc::traceable {
18+
public:
19+
enum { WIDTH = 32 };
20+
tlm::scc::initiator_mixin<tlm::tlm_initiator_socket<WIDTH>> isck{"isck"};
21+
apb::pin::initiator<WIDTH> intor{"intor"};
22+
sc_core::sc_clock PCLK{"PCLK", 1_ns};
23+
sc_core::sc_signal<bool> PRESETn{"PRESETn"};
24+
sc_core::sc_signal<sc_dt::sc_uint<32>> PADDR{"PADDR"};
25+
sc_core::sc_signal<sc_dt::sc_uint<3>> PPROT{"PPROT"};
26+
sc_core::sc_signal<bool> PNSE{"PNSE"};
27+
sc_core::sc_signal<bool> PSELx{"PSELx"};
28+
sc_core::sc_signal<bool> PENABLE{"PENABLE"};
29+
sc_core::sc_signal<bool> PWRITE{"PWRITE"};
30+
sc_core::sc_signal<sc_dt::sc_uint<WIDTH>> PWDATA{"PWDATA"};
31+
sc_core::sc_signal<sc_dt::sc_uint<WIDTH / 8>> PSTRB{"PSTRB"};
32+
sc_core::sc_signal<bool> PREADY{"PREADY"};
33+
sc_core::sc_signal<sc_dt::sc_uint<WIDTH>> PRDATA{"PRDATA"};
34+
sc_core::sc_signal<bool> PSLVERR{"PSLVERR"};
35+
36+
apb::pin::target<WIDTH, 32> target{"target"};
37+
tlm::scc::target_mixin<tlm::tlm_target_socket<scc::LT>> tsck{"tsck"};
38+
39+
testbench(sc_module_name nm)
40+
: sc_module(nm) {
41+
SC_HAS_PROCESS(testbench);
42+
isck(intor.tsckt);
43+
intor.PCLK_i(PCLK);
44+
intor.PRESETn_i(PRESETn);
45+
intor.PADDR_o(PADDR);
46+
intor.PPROT_o(PPROT);
47+
intor.PNSE_o(PNSE);
48+
intor.PSELx_o(PSELx);
49+
intor.PENABLE_o(PENABLE);
50+
intor.PWRITE_o(PWRITE);
51+
intor.PWDATA_o(PWDATA);
52+
intor.PSTRB_o(PSTRB);
53+
intor.PREADY_i(PREADY);
54+
intor.PRDATA_i(PRDATA);
55+
intor.PSLVERR_i(PSLVERR);
56+
target.PCLK_i(PCLK);
57+
target.PRESETn_i(PRESETn);
58+
target.PADDR_i(PADDR);
59+
target.PPROT_i(PPROT);
60+
target.PNSE_i(PNSE);
61+
target.PSELx_i(PSELx);
62+
target.PENABLE_i(PENABLE);
63+
target.PWRITE_i(PWRITE);
64+
target.PWDATA_i(PWDATA);
65+
target.PSTRB_i(PSTRB);
66+
target.PRDATA_o(PRDATA);
67+
target.PREADY_o(PREADY);
68+
target.PSLVERR_o(PSLVERR);
69+
target.isckt(tsck);
70+
SC_THREAD(run);
71+
tsck.register_b_transport([this](tlm::tlm_generic_payload& gp, sc_time& delay) {
72+
gp.set_response_status(tlm::TLM_OK_RESPONSE);
73+
if(gp.is_write()) {
74+
SCCDEBUG(SCMOD) << "Received write access to addr 0x" << std::hex << gp.get_address();
75+
// Print all data bytes with their byte enable values
76+
if(gp.get_byte_enable_length() > 0) {
77+
SCCDEBUG(SCMOD) << " Data with byte enables:";
78+
for(size_t i = 0; i < gp.get_data_length(); ++i) {
79+
SCCDEBUG(SCMOD) << " Byte[" << i << "]: data=0x" << std::hex << std::setw(2) << std::setfill('0')
80+
<< (unsigned)gp.get_data_ptr()[i] << " BE=0x" << std::hex << std::setw(2) << std::setfill('0')
81+
<< (unsigned)gp.get_byte_enable_ptr()[i]
82+
<< (gp.get_byte_enable_ptr()[i] ? " (enabled)" : " (disabled)");
83+
}
84+
} else {
85+
SCCDEBUG(SCMOD) << " Data (no byte enables):";
86+
for(size_t i = 0; i < gp.get_data_length(); ++i) {
87+
SCCDEBUG(SCMOD) << " Byte[" << i << "]: data=0x" << std::hex << std::setw(2) << std::setfill('0')
88+
<< (unsigned)gp.get_data_ptr()[i];
89+
}
90+
}
91+
} else {
92+
memset(gp.get_data_ptr(), 0x55, gp.get_data_length());
93+
SCCDEBUG(SCMOD) << "Received read access from addr 0x" << std::hex << gp.get_address();
94+
}
95+
});
96+
}
97+
98+
void run() {
99+
///////////////////////////////////////////////////////////////////////////
100+
// Test data preparation
101+
///////////////////////////////////////////////////////////////////////////
102+
std::array<uint8_t, 8> write_data;
103+
std::array<uint8_t, 8> read_data;
104+
std::array<uint8_t, 4> byte_enable;
105+
106+
write_data[0] = 0xAA;
107+
write_data[1] = 0xBB;
108+
write_data[2] = 0xCC;
109+
write_data[3] = 0xDD;
110+
write_data[4] = 0xEE;
111+
write_data[5] = 0xFF;
112+
write_data[6] = 0x66;
113+
write_data[7] = 0x77;
114+
115+
///////////////////////////////////////////////////////////////////////////
116+
// Reset sequence
117+
///////////////////////////////////////////////////////////////////////////
118+
PRESETn.write(false);
119+
for(size_t i = 0; i < 10; ++i)
120+
wait(PCLK.posedge_event());
121+
PRESETn.write(true);
122+
wait(PCLK.posedge_event());
123+
124+
///////////////////////////////////////////////////////////////////////////
125+
// Test 1: Pin-level target - Aligned write (full word, all strobes)
126+
///////////////////////////////////////////////////////////////////////////
127+
PENABLE.write(0);
128+
129+
for(int i = 0; i < 2; i++) {
130+
// setup phase
131+
PSELx.write(1);
132+
PADDR.write(0x1000);
133+
PWDATA.write(*reinterpret_cast<uint32_t*>(write_data.data() + i * 4));
134+
PWRITE.write(1);
135+
PSTRB.write(0b1111);
136+
wait(PCLK.posedge_event());
137+
138+
// access phase
139+
PENABLE.write(1);
140+
while(!PREADY.read())
141+
wait(PREADY.value_changed_event());
142+
wait(PCLK.posedge_event());
143+
144+
// cleanup
145+
PENABLE.write(0);
146+
PSELx.write(0);
147+
}
148+
149+
///////////////////////////////////////////////////////////////////////////
150+
// Test 2: Pin-level target - Aligned read (full word)
151+
///////////////////////////////////////////////////////////////////////////
152+
for(int i = 0; i < 2; i++) {
153+
// setup phase
154+
PSELx.write(1);
155+
PADDR.write(0x1004);
156+
PWRITE.write(0);
157+
PSTRB.write(0);
158+
wait(PCLK.posedge_event());
159+
160+
// access phase
161+
PENABLE.write(1);
162+
while(!PREADY.read())
163+
wait(PREADY.value_changed_event());
164+
wait(PCLK.posedge_event());
165+
166+
// cleanup
167+
PENABLE.write(0);
168+
PSELx.write(0);
169+
170+
SCCDEBUG(SCMOD) << std::hex << "read data : " << PRDATA.read();
171+
}
172+
173+
///////////////////////////////////////////////////////////////////////////
174+
// Test 3: Pin-level target - Write with alternating byte strobes (0b1010)
175+
///////////////////////////////////////////////////////////////////////////
176+
// setup phase
177+
PSELx.write(1);
178+
PADDR.write(0x1008);
179+
PWDATA.write(*reinterpret_cast<uint32_t*>(write_data.data()));
180+
PWRITE.write(1);
181+
PSTRB.write(0b1010);
182+
wait(PCLK.posedge_event());
183+
184+
// access phase
185+
PENABLE.write(1);
186+
while(!PREADY.read())
187+
wait(PREADY.value_changed_event());
188+
wait(PCLK.posedge_event());
189+
190+
// cleanup
191+
PENABLE.write(0);
192+
PSELx.write(0);
193+
194+
wait(PCLK.posedge_event());
195+
196+
///////////////////////////////////////////////////////////////////////////
197+
// Test 4: TLM initiator via pin - Write with byte enables (BE: 0xFF, 0x00, 0x00, 0xFF)
198+
///////////////////////////////////////////////////////////////////////////
199+
tlm::tlm_generic_payload gp;
200+
sc_time delay;
201+
202+
byte_enable[0] = 0xFF;
203+
byte_enable[1] = 0x00;
204+
byte_enable[2] = 0x00;
205+
byte_enable[3] = 0xFF;
206+
207+
gp.set_address(0x1010);
208+
gp.set_data_length(4);
209+
gp.set_data_ptr(write_data.data() + 2);
210+
gp.set_byte_enable_ptr(byte_enable.data());
211+
gp.set_byte_enable_length(4);
212+
gp.set_streaming_width(4);
213+
gp.set_command(tlm::TLM_WRITE_COMMAND);
214+
delay = SC_ZERO_TIME;
215+
isck->b_transport(gp, delay);
216+
217+
///////////////////////////////////////////////////////////////////////////
218+
// Test 5: TLM initiator via pin - Aligned read (full word)
219+
///////////////////////////////////////////////////////////////////////////
220+
gp.set_address(0x1014);
221+
gp.set_data_length(4);
222+
gp.set_data_ptr(read_data.data());
223+
gp.set_streaming_width(4);
224+
gp.set_command(tlm::TLM_READ_COMMAND);
225+
delay = SC_ZERO_TIME;
226+
isck->b_transport(gp, delay);
227+
SCCDEBUG(SCMOD) << std::hex << "read data : " << *(uint32_t*)read_data.data();
228+
229+
///////////////////////////////////////////////////////////////////////////
230+
// Test 6: TLM initiator via pin - Unaligned partial write
231+
// Note: Unaligned write (0x1016-0x1018) is converted to strobe write (0x1014-0x1018)
232+
///////////////////////////////////////////////////////////////////////////
233+
gp.set_address(0x1016);
234+
gp.set_data_length(2);
235+
gp.set_data_ptr(write_data.data() + 2);
236+
gp.set_byte_enable_ptr(nullptr);
237+
gp.set_byte_enable_length(0);
238+
gp.set_streaming_width(2);
239+
gp.set_command(tlm::TLM_WRITE_COMMAND);
240+
delay = SC_ZERO_TIME;
241+
isck->b_transport(gp, delay);
242+
243+
///////////////////////////////////////////////////////////////////////////
244+
// Test 7: Invalid case - Transaction exceeding 32-bit boundary (disabled)
245+
// Note: This test is disabled as it tests an invalid scenario
246+
///////////////////////////////////////////////////////////////////////////
247+
if(0) {
248+
gp.set_address(0x1018);
249+
gp.set_data_length(8);
250+
gp.set_data_ptr(read_data.data());
251+
gp.set_streaming_width(8);
252+
gp.set_command(tlm::TLM_READ_COMMAND);
253+
delay = SC_ZERO_TIME;
254+
isck->b_transport(gp, delay);
255+
}
256+
257+
sc_stop();
258+
return;
259+
}
260+
};
261+
262+
int sc_main(int argc, char* argv[]) {
263+
sc_core::sc_report_handler::set_actions("/IEEE_Std_1666/deprecated", sc_core::SC_DO_NOTHING);
264+
sc_report_handler::set_actions(SC_ID_MORE_THAN_ONE_SIGNAL_DRIVER_, SC_DO_NOTHING);
265+
///////////////////////////////////////////////////////////////////////////
266+
// configure logging
267+
///////////////////////////////////////////////////////////////////////////
268+
scc::init_logging(LogConfig().logLevel(scc::log::DEBUG).logAsync(false));
269+
///////////////////////////////////////////////////////////////////////////
270+
// set up configuration and tracing
271+
///////////////////////////////////////////////////////////////////////////
272+
scc::configurer cfg("apb_bfm.json");
273+
scc::configurable_tracer trace("apb_bfm", true, true);
274+
///////////////////////////////////////////////////////////////////////////
275+
// create modules/channels and trace
276+
///////////////////////////////////////////////////////////////////////////
277+
testbench tb("tb");
278+
279+
///////////////////////////////////////////////////////////////////////////
280+
// Create VCD trace file
281+
///////////////////////////////////////////////////////////////////////////
282+
sc_trace_file *vcd_trace = sc_create_vcd_trace_file("apb_trace");
283+
vcd_trace->set_time_unit(1, SC_PS); // Set time unit to picoseconds
284+
285+
// Trace clock and reset
286+
sc_trace(vcd_trace, tb.PCLK, "PCLK");
287+
sc_trace(vcd_trace, tb.PRESETn, "PRESETn");
288+
289+
// Trace APB address phase signals
290+
sc_trace(vcd_trace, tb.PADDR, "PADDR");
291+
sc_trace(vcd_trace, tb.PSELx, "PSELx");
292+
sc_trace(vcd_trace, tb.PENABLE, "PENABLE");
293+
sc_trace(vcd_trace, tb.PWRITE, "PWRITE");
294+
sc_trace(vcd_trace, tb.PPROT, "PPROT");
295+
sc_trace(vcd_trace, tb.PNSE, "PNSE");
296+
297+
// Trace APB data signals
298+
sc_trace(vcd_trace, tb.PWDATA, "PWDATA");
299+
sc_trace(vcd_trace, tb.PSTRB, "PSTRB");
300+
sc_trace(vcd_trace, tb.PRDATA, "PRDATA");
301+
302+
// Trace APB response signals
303+
sc_trace(vcd_trace, tb.PREADY, "PREADY");
304+
sc_trace(vcd_trace, tb.PSLVERR, "PSLVERR");
305+
cfg.configure();
306+
cfg.dump_configuration("apb_test.default.json");
307+
///////////////////////////////////////////////////////////////////////////
308+
// run the simulation
309+
///////////////////////////////////////////////////////////////////////////
310+
try {
311+
sc_core::sc_start();
312+
if(!sc_core::sc_end_of_simulation_invoked())
313+
sc_core::sc_stop();
314+
} catch(sc_report& e) {
315+
SCCERR() << "Caught sc_report exception during simulation: " << e.what() << ":" << e.get_msg();
316+
} catch(std::exception& e) {
317+
SCCERR() << "Caught exception during simulation: " << e.what();
318+
} catch(...) {
319+
SCCERR() << "Caught unspecified exception during simulation";
320+
}
321+
322+
///////////////////////////////////////////////////////////////////////////
323+
// Close VCD trace file
324+
///////////////////////////////////////////////////////////////////////////
325+
sc_close_vcd_trace_file(vcd_trace);
326+
327+
return 0;
328+
}

0 commit comments

Comments
 (0)