|
| 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