diff --git a/scc b/scc index 06ec2f7..621a069 160000 --- a/scc +++ b/scc @@ -1 +1 @@ -Subproject commit 06ec2f720792ce0129430ab0db6ed408ac476457 +Subproject commit 621a0695aa072ec934c57d0894454aca08d3ccf7 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 5e98656..0d161b8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(io-redirector) add_subdirectory(ordered_semaphore) add_subdirectory(cci_param_restricted) +add_subdirectory(apb_pin_level) add_subdirectory(ahb_pin_level) add_subdirectory(axi4_pin_level) add_subdirectory(ace_pin_level) diff --git a/tests/apb_pin_level/CMakeLists.txt b/tests/apb_pin_level/CMakeLists.txt new file mode 100644 index 0000000..bd52313 --- /dev/null +++ b/tests/apb_pin_level/CMakeLists.txt @@ -0,0 +1,9 @@ +project (apb_pin_level) + +add_executable(${PROJECT_NAME} + bus_test.cpp + ${test_util_SOURCE_DIR}/sc_main.cpp +) +target_link_libraries (${PROJECT_NAME} PUBLIC test_util) + +catch_discover_tests(${PROJECT_NAME}) diff --git a/tests/apb_pin_level/bus_test.cpp b/tests/apb_pin_level/bus_test.cpp new file mode 100644 index 0000000..3cdff2f --- /dev/null +++ b/tests/apb_pin_level/bus_test.cpp @@ -0,0 +1,255 @@ + +#include "testbench.h" +#include +#include +#undef CHECK +#include +#include + +using namespace sc_core; +using namespace ahb; + +factory::add tb; +bool is_equal(tlm::tlm_generic_payload const& a, tlm::tlm_generic_payload const& b) { + auto ret = true; + ret &= a.get_command() == b.get_command(); + ret &= a.get_address() == b.get_address(); + ret &= a.get_data_length() == b.get_data_length(); + for(auto i = 0u; i < a.get_data_length(); ++i) + ret &= a.get_data_ptr()[i] == b.get_data_ptr()[i]; + // if(a.get_byte_enable_ptr() && b.get_byte_enable_ptr()) { + // ret &= a.get_byte_enable_length() == b.get_byte_enable_length(); + // for(auto i=0u; i tlm::tlm_generic_payload* prepare_trans(uint64_t start_address, unsigned len, unsigned width) { + static unsigned id{0}; + auto trans = tlm::scc::tlm_mm<>::get().allocate(len); + trans->set_address(start_address); + tlm::scc::setId(*trans, ++id); + auto ext = trans->get_extension(); + trans->set_data_length(len); + trans->set_streaming_width(len); + ext->set_instruction(); + return trans; +} + +inline void randomize(tlm::tlm_generic_payload& gp) { + static uint8_t req_cnt{0}; + for(size_t i = 0; i < gp.get_data_length(); ++i) { + *(gp.get_data_ptr() + i) = i % 2 ? i : req_cnt; + } + req_cnt++; +} + +template unsigned run_scenario(STATE& state, unsigned wait_states = 0) { + auto& dut = factory::get(); + dut.register_b_transport([&state, wait_states](tlm::tlm_base_protocol_types::tlm_payload_type& trans, sc_core::sc_time& d) { + if(trans.is_read()) { + for(size_t i = 0; i < trans.get_data_length(); ++i) { + *(trans.get_data_ptr() + i) = i % 2 ? i : (state.resp_cnt + 128); + } + state.read_tx.second.emplace_back(&trans); + } + if(trans.is_write()) + state.write_tx.second.emplace_back(&trans); + SCCDEBUG(__FUNCTION__) << "RX: " << trans; + for(unsigned i = 0; i < wait_states; ++i) + sc_core::wait(factory::get().clk.posedge_event()); + state.resp_cnt++; + trans.set_response_status(tlm::TLM_OK_RESPONSE); + }); + + dut.rst_n.write(false); + sc_start(state.ResetCycles * dut.clk.period()); + dut.rst_n.write(true); + sc_start(dut.clk.period()); + sc_start(dut.clk.period()); + + auto run1 = sc_spawn([&dut, &state]() { + unsigned int StartAddr{0x0}; + for(int i = 0; i < state.NumberOfIterations; ++i) { + tlm::scc::tlm_gp_shared_ptr trans = + prepare_trans(StartAddr, state.BurstLengthByte, state.BurstSizeBytes); + trans->set_command(tlm::TLM_READ_COMMAND); + SCCDEBUG(__FUNCTION__) << "task run1, iteration " << i << " TX: " << *trans; + sc_core::sc_time d; + dut.isck->b_transport(*trans, d); + state.read_tx.first.emplace_back(trans); + StartAddr += state.BurstSizeBytes; + } + SCCDEBUG(__FUNCTION__) << "task run1 finished"; + }); + auto run2 = sc_spawn([&dut, &state]() { + unsigned int StartAddr{0x2000}; + for(int i = 0; i < state.NumberOfIterations; ++i) { + tlm::scc::tlm_gp_shared_ptr trans = + prepare_trans(StartAddr, state.BurstLengthByte, state.BurstSizeBytes); + trans->set_command(tlm::TLM_WRITE_COMMAND); + randomize(*trans); + SCCDEBUG(__FUNCTION__) << "task run2, iteration " << i << " TX: " << *trans; + sc_core::sc_time d; + dut.isck->b_transport(*trans, d); + state.write_tx.first.emplace_back(trans); + StartAddr += state.BurstSizeBytes; + } + SCCDEBUG(__FUNCTION__) << "task run2 finished"; + }); + auto run3 = sc_spawn([&dut, &state]() { + unsigned int StartAddr{0x1000}; + for(int i = 0; i < state.NumberOfIterations; ++i) { + tlm::scc::tlm_gp_shared_ptr trans = + prepare_trans(StartAddr, state.BurstLengthByte, state.BurstSizeBytes); + trans->set_command(tlm::TLM_READ_COMMAND); + SCCDEBUG(__FUNCTION__) << "task run3, iteration " << i << " TX: " << *trans; + sc_core::sc_time d; + dut.isck->b_transport(*trans, d); + state.read_tx.first.emplace_back(trans); + StartAddr += state.BurstSizeBytes; + } + SCCDEBUG(__FUNCTION__) << "task run3 finished"; + }); + auto run4 = sc_spawn([&dut, &state]() { + unsigned int StartAddr{0x3000}; + for(int i = 0; i < state.NumberOfIterations; ++i) { + tlm::scc::tlm_gp_shared_ptr trans = + prepare_trans(StartAddr, state.BurstLengthByte, state.BurstSizeBytes); + trans->set_command(tlm::TLM_WRITE_COMMAND); + randomize(*trans); + SCCDEBUG(__FUNCTION__) << "task run4, iteration " << i << " TX: " << *trans; + sc_core::sc_time d; + dut.isck->b_transport(*trans, d); + state.write_tx.first.emplace_back(trans); + StartAddr += state.BurstSizeBytes; + } + SCCDEBUG(__FUNCTION__) << "task run4 finished"; + }); + + unsigned cycles{0}; + while(cycles < 1000 && !(run1.terminated() && run2.terminated() && run3.terminated() && run4.terminated())) { + sc_start(10 * dut.clk.period()); + cycles += 10; + } + return cycles; +} + +TEST_CASE("apb_read_write", "[APB][pin-level]") { + struct { + unsigned int ResetCycles{4}; + unsigned int BurstLengthByte{4}; + unsigned int BurstSizeBytes{4}; + unsigned int NumberOfIterations{1}; + std::pair, std::vector> read_tx; + std::pair, std::vector> write_tx; + unsigned resp_cnt{0}; + } state; + + auto cycles = run_scenario(state); + + REQUIRE(cycles < 1000); + REQUIRE(sc_report_handler::get_count(SC_ERROR) == 0); + REQUIRE(sc_report_handler::get_count(SC_WARNING) == 0); + + REQUIRE(state.resp_cnt == 4 * state.NumberOfIterations); + { + auto& e = state.write_tx; + auto const& send_tx = e.first; + auto const& recv_tx = e.second; + REQUIRE(send_tx.size() == recv_tx.size()); + for(auto i = 0; i < send_tx.size(); ++i) { + REQUIRE(send_tx[i]->get_response_status() == tlm::TLM_OK_RESPONSE); + CHECK(is_equal(*send_tx[i], *recv_tx[i])); + } + } + { + auto& e = state.read_tx; + auto const& send_tx = e.first; + auto const& recv_tx = e.second; + REQUIRE(send_tx.size() == recv_tx.size()); + for(auto i = 0; i < send_tx.size(); ++i) { + REQUIRE(send_tx[i]->get_response_status() == tlm::TLM_OK_RESPONSE); + CHECK(is_equal(*send_tx[i], *recv_tx[i])); + } + } +} + +TEST_CASE("apb_narrow_read_write", "[APB][pin-level]") { + struct { + unsigned int ResetCycles{4}; + unsigned int BurstLengthByte{1}; + unsigned int BurstSizeBytes{1}; + unsigned int NumberOfIterations{8}; + std::pair, std::vector> read_tx; + std::pair, std::vector> write_tx; + unsigned resp_cnt{0}; + } state; + + auto cycles = run_scenario(state); + + REQUIRE(cycles < 1000); + REQUIRE(sc_report_handler::get_count(SC_ERROR) == 0); + REQUIRE(sc_report_handler::get_count(SC_WARNING) == 0); + + REQUIRE(state.resp_cnt == 4 * state.NumberOfIterations); + { + auto& e = state.write_tx; + auto const& send_tx = e.first; + auto const& recv_tx = e.second; + REQUIRE(send_tx.size() == recv_tx.size()); + for(auto i = 0; i < send_tx.size(); ++i) + CHECK(is_equal(*send_tx[i], *recv_tx[i])); + } + { + auto& e = state.read_tx; + auto const& send_tx = e.first; + auto const& recv_tx = e.second; + REQUIRE(send_tx.size() == recv_tx.size()); + // Narrow reads cannot be checked as they arrive a word read at the target + // for(auto i = 0; i < send_tx.size(); ++i) + // CHECK(is_equal(*send_tx[i], *recv_tx[i])); + } +} + +TEST_CASE("apb_delayed_read_write", "[APB][pin-level]") { + struct { + unsigned int ResetCycles{4}; + unsigned int BurstLengthByte{4}; + unsigned int BurstSizeBytes{4}; + unsigned int NumberOfIterations{2}; + std::pair, std::vector> read_tx; + std::pair, std::vector> write_tx; + unsigned resp_cnt{0}; + } state; + + auto cycles = run_scenario(state, 2); + + REQUIRE(cycles < 1000); + REQUIRE(sc_report_handler::get_count(SC_ERROR) == 0); + REQUIRE(sc_report_handler::get_count(SC_WARNING) == 0); + + REQUIRE(state.resp_cnt == 4 * state.NumberOfIterations); + { + auto& e = state.write_tx; + auto const& send_tx = e.first; + auto const& recv_tx = e.second; + REQUIRE(send_tx.size() == recv_tx.size()); + for(auto i = 0; i < send_tx.size(); ++i) { + REQUIRE(send_tx[i]->get_response_status() == tlm::TLM_OK_RESPONSE); + CHECK(is_equal(*send_tx[i], *recv_tx[i])); + } + } + { + auto& e = state.read_tx; + auto const& send_tx = e.first; + auto const& recv_tx = e.second; + REQUIRE(send_tx.size() == recv_tx.size()); + for(auto i = 0; i < send_tx.size(); ++i) { + REQUIRE(send_tx[i]->get_response_status() == tlm::TLM_OK_RESPONSE); + CHECK(is_equal(*send_tx[i], *recv_tx[i])); + } + } +} diff --git a/tests/apb_pin_level/testbench.h b/tests/apb_pin_level/testbench.h new file mode 100644 index 0000000..bb14d26 --- /dev/null +++ b/tests/apb_pin_level/testbench.h @@ -0,0 +1,89 @@ +#ifndef _TESTBENCH_H_ +#define _TESTBENCH_H_ + +#include +#include +#include + +using namespace sc_core; + +class testbench : public sc_core::sc_module { +public: + enum { DATA_WIDTH = 32, ADDR_WIDTH = 32 }; + using addr_t = typename apb::pin::initiator::addr_t; + using data_t = apb::pin::initiator::data_t; + using strb_t = sc_dt::sc_uint; + sc_core::sc_time clk_period{10, sc_core::SC_NS}; + sc_core::sc_clock clk{"clk", clk_period, 0.5, sc_core::SC_ZERO_TIME, true}; + sc_core::sc_signal rst_n{"rst_n"}; + // initiator side + tlm::scc::initiator_mixin> isck{"isck"}; + apb::pin::initiator intor_bfm{"intor_bfm"}; + // signal accurate bus + sc_core::sc_signal PADDR{"PADDR"}; + sc_core::sc_signal> PPROT{"PPROT"}; + sc_core::sc_signal PNSE{"PNSE"}; + sc_core::sc_signal PSELx{"PSELx"}; + sc_core::sc_signal PENABLE{"PENABLE"}; + sc_core::sc_signal PWRITE{"PWRITE"}; + sc_core::sc_signal PWDATA{"PWDATA"}; + sc_core::sc_signal PSTRB{"PSTRB"}; + sc_core::sc_signal PREADY{"PREADY"}; + sc_core::sc_signal PRDATA{"PRDATA"}; + sc_core::sc_signal PSLVERR{"PSLVERR"}; + sc_core::sc_signal PWAKEUP{"PWAKEUP"}; + // target side + apb::pin::target tgt_bfm{"tgt_bfm"}; + tlm::scc::target_mixin> tsck{"tsck"}; + +public: + SC_HAS_PROCESS(testbench); + testbench() + : testbench("testbench") {} + testbench(sc_core::sc_module_name nm) + : sc_core::sc_module(nm) { + intor_bfm.PCLK_i(clk); + tgt_bfm.PCLK_i(clk); + // bfm to signals + isck(intor_bfm.tsckt); + intor_bfm.PRESETn_i(rst_n); + intor_bfm.PADDR_o(PADDR); + intor_bfm.PPROT_o(PPROT); + intor_bfm.PNSE_o(PNSE); + intor_bfm.PSELx_o(PSELx); + intor_bfm.PENABLE_o(PENABLE); + intor_bfm.PWRITE_o(PWRITE); + intor_bfm.PWDATA_o(PWDATA); + intor_bfm.PSTRB_o(PSTRB); + intor_bfm.PREADY_i(PREADY); + intor_bfm.PRDATA_i(PRDATA); + intor_bfm.PSLVERR_i(PSLVERR); + intor_bfm.PWAKEUP_o(PWAKEUP); + + tgt_bfm.PRESETn_i(rst_n); + tgt_bfm.PADDR_i(PADDR); + tgt_bfm.PPROT_i(PPROT); + tgt_bfm.PNSE_i(PNSE); + tgt_bfm.PSELx_i(PSELx); + tgt_bfm.PENABLE_i(PENABLE); + tgt_bfm.PWRITE_i(PWRITE); + tgt_bfm.PWDATA_i(PWDATA); + tgt_bfm.PSTRB_i(PSTRB); + tgt_bfm.PREADY_o(PREADY); + tgt_bfm.PRDATA_o(PRDATA); + tgt_bfm.PSLVERR_o(PSLVERR); + tgt_bfm.PWAKEUP_i(PWAKEUP); + + tgt_bfm.isckt(tsck); + tsck.register_b_transport([this](tlm::tlm_base_protocol_types::tlm_payload_type& trans, sc_core::sc_time& d) { + if(cb_delegate) + cb_delegate(trans, d); + }); + } + + void run1() {} + void register_b_transport(std::function cb) { cb_delegate = cb; } + std::function cb_delegate; +}; + +#endif // _TESTBENCH_H_