diff --git a/cmake/SystemCPackage.cmake b/cmake/SystemCPackage.cmake index a7eb6557..a21c30a3 100644 --- a/cmake/SystemCPackage.cmake +++ b/cmake/SystemCPackage.cmake @@ -1,38 +1,40 @@ # a small wrapper to map SLS, OSCI SystemC (from environment) and conan package to the same variables if(NOT SystemC_FOUND) - if(USE_CWR_SYSTEMC) - find_package(SLSSystemC REQUIRED) - elseif(USE_NCSC_SYSTEMC) - find_package(XMSystemC REQUIRED) - else() - find_package(SystemCLanguage QUIET) - if(TARGET SystemC::systemc) # conan find_package_generator or the cmake of an SystemC installation - set(SystemC_FOUND true) - set(SystemC_LIBRARIES SystemC::systemc) - find_package(systemc-scv QUIET) - if(systemc-scv_FOUND) - set(SCV_FOUND TRUE) - set(SCV_LIBRARIES systemc-scv::systemc-scv) - endif() - find_package(systemc-cci QUIET) - if(systemc-cci_FOUND) - set(CCI_FOUND TRUE) - set(CCI_LIBRARIES systemc-cci::systemc-cci) - endif() - elseif(TARGET CONAN_PKG::systemc) # conan cmake targets - set(SystemC_FOUND true) - set(SystemC_LIBRARIES CONAN_PKG::systemc) - if(TARGET CONAN_PKG::systemc-scv) - set(SCV_FOUND TRUE) - set(SCV_LIBRARIES CONAN_PKG::systemc-scv) - endif() - if(TARGET CONAN_PKG::systemc-cci) - set(CCI_FOUND TRUE) - set(CCI_LIBRARIES CONAN_PKG::systemc-cci) - endif() - else() - find_package(OSCISystemC) + if(USE_CWR_SYSTEMC) + find_package(SLSSystemC REQUIRED) + set(SystemC_LIBRARIES SystemC::systemc) + elseif(USE_NCSC_SYSTEMC) + find_package(XMSystemC REQUIRED) + set(SystemC_LIBRARIES SystemC::systemc) + else() + find_package(SystemCLanguage QUIET) + if(TARGET SystemC::systemc) # conan find_package_generator or the cmake of an SystemC installation + set(SystemC_FOUND true) set(SystemC_LIBRARIES SystemC::systemc) - endif() - endif() + find_package(systemc-scv QUIET) + if(systemc-scv_FOUND) + set(SCV_FOUND TRUE) + set(SCV_LIBRARIES systemc-scv::systemc-scv) + endif() + find_package(systemc-cci QUIET) + if(systemc-cci_FOUND) + set(CCI_FOUND TRUE) + set(CCI_LIBRARIES systemc-cci::systemc-cci) + endif() + elseif(TARGET CONAN_PKG::systemc) # conan cmake targets + set(SystemC_FOUND true) + set(SystemC_LIBRARIES CONAN_PKG::systemc) + if(TARGET CONAN_PKG::systemc-scv) + set(SCV_FOUND TRUE) + set(SCV_LIBRARIES CONAN_PKG::systemc-scv) + endif() + if(TARGET CONAN_PKG::systemc-cci) + set(CCI_FOUND TRUE) + set(CCI_LIBRARIES CONAN_PKG::systemc-cci) + endif() + else() + find_package(OSCISystemC) + set(SystemC_LIBRARIES SystemC::systemc) + endif() + endif() endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b097a1bc..8492acc4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -3,5 +3,11 @@ if(NOT USE_CWR_SYSTEMC) add_subdirectory(axi-axi) add_subdirectory(axi4_tlm-pin-tlm) add_subdirectory(axi4lite_tlm-pin-tlm) + add_subdirectory(ahb_bfm) + add_subdirectory(simple_system) + if(ENABLE_SCV AND SCV_FOUND) + add_subdirectory(transaction_recording) + endif() + add_subdirectory(scc-tlm_target_bfs) endif() -add_subdirectory(scc-tlm_target_bfs) + diff --git a/examples/ahb_bfm/CMakeLists.txt b/examples/ahb_bfm/CMakeLists.txt new file mode 100644 index 00000000..26a00a3b --- /dev/null +++ b/examples/ahb_bfm/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.3) +# Add executable called "transaction_recording" that is built from the source files +# "scv_tr_recording_example.cpp". The extensions are automatically found. +add_executable (ahb_bfm + sc_main.cpp +) +# Link the executable to the sc_components library. Since the sc_components library has +# public include directories we will use those link directories when building +# transaction_recording +target_link_libraries (ahb_bfm LINK_PUBLIC scc) diff --git a/examples/ahb_bfm/sc_main.cpp b/examples/ahb_bfm/sc_main.cpp new file mode 100644 index 00000000..ca278c67 --- /dev/null +++ b/examples/ahb_bfm/sc_main.cpp @@ -0,0 +1,145 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace sc_core; +using namespace scc; + +class testbench: public sc_module, public scc::traceable { +public: + enum { WIDTH=64}; + tlm::scc::initiator_mixin> isck{"isck"}; + ahb::pin::initiator intor{"intor"}; + sc_core::sc_clock HCLK{"HCLK", 10_ns}; + sc_core::sc_signal HRESETn{"HRESETn"}; + sc_core::sc_signal> HADDR{"HADDR"}; + sc_core::sc_signal> HBURST{"HBURST"}; + sc_core::sc_signal HMASTLOCK{"HMASTLOCK"}; + sc_core::sc_signal> HPROT{"HPROT"}; + sc_core::sc_signal> HSIZE{"HSIZE"}; + sc_core::sc_signal> HTRANS{"HTRANS"}; + sc_core::sc_signal> HWDATA{"HWDATA"}; + sc_core::sc_signal HWRITE{"HWRITE"}; + sc_core::sc_signal> HRDATA{"HRDATA"}; + sc_core::sc_signal HREADY{"HREADY"}; + sc_core::sc_signal HRESP{"HRESP"}; + sc_core::sc_signal HSEL{"HSEL"}; + + ahb::pin::target target{"target"}; + tlm::scc::target_mixin> tsck{"tsck"}; + + testbench(sc_module_name nm):sc_module(nm){ + SC_HAS_PROCESS(testbench); + isck(intor.tsckt); + intor.HCLK_i(HCLK); + intor.HRESETn_i(HRESETn); + intor.HADDR_o(HADDR); + intor.HBURST_o(HBURST); + intor.HMASTLOCK_o(HMASTLOCK); + intor.HPROT_o(HPROT); + intor.HSIZE_o(HSIZE); + intor.HTRANS_o(HTRANS); + intor.HWDATA_o(HWDATA); + intor.HWRITE_o(HWRITE); + intor.HRDATA_i(HRDATA); + intor.HREADY_i(HREADY); + intor.HRESP_i(HRESP); + target.HCLK_i(HCLK); + target.HRESETn_i(HRESETn); + target.HADDR_i(HADDR); + target.HBURST_i(HBURST); + target.HMASTLOCK_i(HMASTLOCK); + target.HPROT_i(HPROT); + target.HSIZE_i(HSIZE); + target.HTRANS_i(HTRANS); + target.HWDATA_i(HWDATA); + target.HWRITE_i(HWRITE); + target.HSEL_i(HSEL); + target.HRDATA_o(HRDATA); + target.HREADY_o(HREADY); + target.HRESP_o(HRESP); + target.isckt(tsck); + SC_THREAD(run); + tsck.register_b_transport([this](tlm::tlm_generic_payload& gp, sc_time& delay){ + gp.set_response_status(tlm::TLM_OK_RESPONSE); + if(gp.is_write()){ + SCCINFO(SCMOD)<<"Received write access to addr 0x"< data; + data[0]=2; + data[1]=4; + gp.set_address(0x1000); + gp.set_data_length(8); + gp.set_data_ptr(data.data()); + gp.set_streaming_width(8); + gp.set_command(tlm::TLM_WRITE_COMMAND); + sc_time delay; + isck->b_transport(gp, delay); + gp.set_address(0x1020); + gp.set_data_length(8); + gp.set_data_ptr(data.data()); + gp.set_streaming_width(8); + gp.set_command(tlm::TLM_READ_COMMAND); + delay=SC_ZERO_TIME; + isck->b_transport(gp, delay); + for(size_t i=0; i<10; ++i) wait(HCLK.posedge_event()); + sc_stop(); + } + +}; + +int sc_main (int argc , char *argv[]){ + sc_core::sc_report_handler::set_actions( "/IEEE_Std_1666/deprecated", sc_core::SC_DO_NOTHING ); + sc_report_handler::set_actions(SC_ID_MORE_THAN_ONE_SIGNAL_DRIVER_, SC_DO_NOTHING); + /////////////////////////////////////////////////////////////////////////// + // configure logging + /////////////////////////////////////////////////////////////////////////// + scc::init_logging(scc::log::DEBUG); + /////////////////////////////////////////////////////////////////////////// + // set up configuration and tracing + /////////////////////////////////////////////////////////////////////////// + scc::configurer cfg("ahb_bfm.json"); + scc::configurable_tracer trace("ahb_bfm", tracer::TEXT, true, true); + /////////////////////////////////////////////////////////////////////////// + // create modules/channels and trace + /////////////////////////////////////////////////////////////////////////// + testbench tb("tb"); + trace.add_control(); + { + std::ofstream of{"ahb_test.default.json"}; + if (of.is_open()) cfg.dump_configuration(of); + } + cfg.configure(); + /////////////////////////////////////////////////////////////////////////// + // run the simulation + /////////////////////////////////////////////////////////////////////////// + try { + sc_core::sc_start(1_us); + if (!sc_core::sc_end_of_simulation_invoked()) sc_core::sc_stop(); + } catch (sc_core::sc_report &rep) { + sc_core::sc_report_handler::get_handler()(rep, sc_core::SC_DISPLAY | sc_core::SC_STOP); + } + return 0; +} + diff --git a/examples/simple_system/CMakeLists.txt b/examples/simple_system/CMakeLists.txt new file mode 100644 index 00000000..ee8c6732 --- /dev/null +++ b/examples/simple_system/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.12) +find_package(Boost COMPONENTS program_options REQUIRED) +add_executable (simple_system + plic.cpp + uart.cpp + spi.cpp + gpio.cpp + test_initiator.cpp + simple_system.cpp + sc_main.cpp +) +target_link_libraries (simple_system LINK_PUBLIC scc) +if(TARGET Boost::program_options) + target_link_libraries(simple_system PUBLIC Boost::program_options) +else() + target_link_libraries(simple_system PUBLIC ${BOOST_program_options_LIBRARY}) +endif() +add_test(NAME simple_system_test COMMAND simple_system) \ No newline at end of file diff --git a/examples/simple_system/gen/e300_plat_t.h b/examples/simple_system/gen/e300_plat_t.h new file mode 100644 index 00000000..57ce8755 --- /dev/null +++ b/examples/simple_system/gen/e300_plat_t.h @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright 2017 eyck@minres.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +#ifndef _E300_PLAT_MAP_H_ +#define _E300_PLAT_MAP_H_ +// need double braces, see +// https://stackoverflow.com/questions/6893700/how-to-construct-stdarray-object-with-initializer-list#6894191 +const std::array, 4> e300_plat_map = {{ + {"plic", 0x0c000000, 0x200008}, + {"gpio", 0x10012000, 0x1000}, + {"uart", 0x10013000, 0x1000}, + {"spi", 0x10014000, 0x1000}, +}}; + +#endif /* _E300_PLAT_MAP_H_ */ diff --git a/examples/simple_system/gen/gpio_regs.h b/examples/simple_system/gen/gpio_regs.h new file mode 100644 index 00000000..541adf92 --- /dev/null +++ b/examples/simple_system/gen/gpio_regs.h @@ -0,0 +1,152 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Created on: Wed Sep 20 11:47:24 CEST 2017 +// * gpio_regs.h Author: +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _GPIO_REGS_H_ +#define _GPIO_REGS_H_ + +#include +#include "scc/register.h" +#include "scc/tlm_target.h" +#include "scc/utilities.h" + +namespace sysc { + +class gpio_regs : public sc_core::sc_module, public scc::resetable { +public: + // storage declarations + uint32_t r_value; + + uint32_t r_input_en; + + uint32_t r_output_en; + + uint32_t r_port; + + uint32_t r_pue; + + uint32_t r_ds; + + uint32_t r_rise_ie; + + uint32_t r_rise_ip; + + uint32_t r_fall_ie; + + uint32_t r_fall_ip; + + uint32_t r_high_ie; + + uint32_t r_high_ip; + + uint32_t r_low_ie; + + uint32_t r_low_ip; + + uint32_t r_iof_en; + + uint32_t r_iof_sel; + + uint32_t r_out_xor; + + // register declarations + scc::sc_register value; + scc::sc_register input_en; + scc::sc_register output_en; + scc::sc_register port; + scc::sc_register pue; + scc::sc_register ds; + scc::sc_register rise_ie; + scc::sc_register rise_ip; + scc::sc_register fall_ie; + scc::sc_register fall_ip; + scc::sc_register high_ie; + scc::sc_register high_ip; + scc::sc_register low_ie; + scc::sc_register low_ip; + scc::sc_register iof_en; + scc::sc_register iof_sel; + scc::sc_register out_xor; + +public: + gpio_regs(sc_core::sc_module_name nm); + + template void registerResources(scc::tlm_target &target); +}; +} +////////////////////////////////////////////////////////////////////////////// +// member functions +////////////////////////////////////////////////////////////////////////////// + +inline sysc::gpio_regs::gpio_regs(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, NAMED(value, r_value, 0, *this) +, NAMED(input_en, r_input_en, 0, *this) +, NAMED(output_en, r_output_en, 0, *this) +, NAMED(port, r_port, 0, *this) +, NAMED(pue, r_pue, 0, *this) +, NAMED(ds, r_ds, 0, *this) +, NAMED(rise_ie, r_rise_ie, 0, *this) +, NAMED(rise_ip, r_rise_ip, 0, *this) +, NAMED(fall_ie, r_fall_ie, 0, *this) +, NAMED(fall_ip, r_fall_ip, 0, *this) +, NAMED(high_ie, r_high_ie, 0, *this) +, NAMED(high_ip, r_high_ip, 0, *this) +, NAMED(low_ie, r_low_ie, 0, *this) +, NAMED(low_ip, r_low_ip, 0, *this) +, NAMED(iof_en, r_iof_en, 0, *this) +, NAMED(iof_sel, r_iof_sel, 0, *this) +, NAMED(out_xor, r_out_xor, 0, *this) {} + +template inline void sysc::gpio_regs::registerResources(scc::tlm_target &target) { + target.addResource(value, 0x0UL); + target.addResource(input_en, 0x4UL); + target.addResource(output_en, 0x8UL); + target.addResource(port, 0xcUL); + target.addResource(pue, 0x10UL); + target.addResource(ds, 0x14UL); + target.addResource(rise_ie, 0x18UL); + target.addResource(rise_ip, 0x1cUL); + target.addResource(fall_ie, 0x20UL); + target.addResource(fall_ip, 0x24UL); + target.addResource(high_ie, 0x28UL); + target.addResource(high_ip, 0x2cUL); + target.addResource(low_ie, 0x30UL); + target.addResource(low_ip, 0x34UL); + target.addResource(iof_en, 0x38UL); + target.addResource(iof_sel, 0x3cUL); + target.addResource(out_xor, 0x40UL); +} + +#endif // _GPIO_REGS_H_ diff --git a/examples/simple_system/gen/plic_regs.h b/examples/simple_system/gen/plic_regs.h new file mode 100644 index 00000000..68643281 --- /dev/null +++ b/examples/simple_system/gen/plic_regs.h @@ -0,0 +1,96 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Created on: Wed Sep 20 22:30:45 CEST 2017 +// * plic_regs.h Author: +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _PLIC_REGS_H_ +#define _PLIC_REGS_H_ + +#include +#include "scc/register.h" +#include "scc/tlm_target.h" +#include "scc/utilities.h" + +namespace sysc { + +class plic_regs : public sc_core::sc_module, public scc::resetable { +public: + // storage declarations + BEGIN_BF_DECL(priority_t, uint32_t); + BF_FIELD(priority, 0, 3); + END_BF_DECL(); + std::array r_priority; + + uint32_t r_pending; + + uint32_t r_enabled; + + BEGIN_BF_DECL(threshold_t, uint32_t); + BF_FIELD(threshold, 0, 3); + END_BF_DECL() r_threshold; + + uint32_t r_claim_complete; + + // register declarations + scc::sc_register_indexed priority; + scc::sc_register pending; + scc::sc_register enabled; + scc::sc_register threshold; + scc::sc_register claim_complete; + + plic_regs(sc_core::sc_module_name nm); + + template void registerResources(scc::tlm_target &target); +}; +} +////////////////////////////////////////////////////////////////////////////// +// member functions +////////////////////////////////////////////////////////////////////////////// + +inline sysc::plic_regs::plic_regs(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, NAMED(priority, r_priority, 0, *this) +, NAMED(pending, r_pending, 0, *this) +, NAMED(enabled, r_enabled, 0, *this) +, NAMED(threshold, r_threshold, 0, *this) +, NAMED(claim_complete, r_claim_complete, 0, *this) {} + +template inline void sysc::plic_regs::registerResources(scc::tlm_target &target) { + target.addResource(priority, 0x4UL); + target.addResource(pending, 0x1000UL); + target.addResource(enabled, 0x2000UL); + target.addResource(threshold, 0x00200000UL); + target.addResource(claim_complete, 0x00200004UL); +} + +#endif // _PLIC_REGS_H_ diff --git a/examples/simple_system/gen/spi_regs.h b/examples/simple_system/gen/spi_regs.h new file mode 100644 index 00000000..871a87a9 --- /dev/null +++ b/examples/simple_system/gen/spi_regs.h @@ -0,0 +1,192 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Created on: Wed Sep 20 22:30:45 CEST 2017 +// * spi_regs.h Author: +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _SPI_REGS_H_ +#define _SPI_REGS_H_ + +#include +#include "scc/register.h" +#include "scc/tlm_target.h" +#include "scc/utilities.h" + +namespace sysc { + +class spi_regs : public sc_core::sc_module, public scc::resetable { +protected: + // storage declarations + BEGIN_BF_DECL(sckdiv_t, uint32_t); + BF_FIELD(div, 0, 12); + END_BF_DECL() r_sckdiv; + + BEGIN_BF_DECL(sckmode_t, uint32_t); + BF_FIELD(pha, 0, 1); + BF_FIELD(pol, 1, 1); + END_BF_DECL() r_sckmode; + + uint32_t r_csid; + + uint32_t r_csdef; + + BEGIN_BF_DECL(csmode_t, uint32_t); + BF_FIELD(mode, 0, 2); + END_BF_DECL() r_csmode; + + BEGIN_BF_DECL(delay0_t, uint32_t); + BF_FIELD(cssck, 0, 8); + BF_FIELD(sckcs, 16, 8); + END_BF_DECL() r_delay0; + + BEGIN_BF_DECL(delay1_t, uint32_t); + BF_FIELD(intercs, 0, 16); + BF_FIELD(interxfr, 16, 8); + END_BF_DECL() r_delay1; + + BEGIN_BF_DECL(fmt_t, uint32_t); + BF_FIELD(proto, 0, 2); + BF_FIELD(endian, 2, 1); + BF_FIELD(dir, 3, 1); + BF_FIELD(len, 16, 4); + END_BF_DECL() r_fmt; + + BEGIN_BF_DECL(txdata_t, uint32_t); + BF_FIELD(data, 0, 8); + BF_FIELD(full, 31, 1); + END_BF_DECL() r_txdata; + + BEGIN_BF_DECL(rxdata_t, uint32_t); + BF_FIELD(data, 0, 8); + BF_FIELD(empty, 31, 1); + END_BF_DECL() r_rxdata; + + BEGIN_BF_DECL(txmark_t, uint32_t); + BF_FIELD(txmark, 0, 3); + END_BF_DECL() r_txmark; + + BEGIN_BF_DECL(rxmark_t, uint32_t); + BF_FIELD(rxmark, 0, 3); + END_BF_DECL() r_rxmark; + + BEGIN_BF_DECL(fctrl_t, uint32_t); + BF_FIELD(en, 0, 1); + END_BF_DECL() r_fctrl; + + BEGIN_BF_DECL(ffmt_t, uint32_t); + BF_FIELD(cmd_en, 0, 1); + BF_FIELD(addr_len, 1, 2); + BF_FIELD(pad_cnt, 3, 4); + BF_FIELD(cmd_proto, 7, 2); + BF_FIELD(addr_proto, 9, 2); + BF_FIELD(data_proto, 11, 2); + BF_FIELD(cmd_code, 16, 8); + BF_FIELD(pad_code, 24, 8); + END_BF_DECL() r_ffmt; + + BEGIN_BF_DECL(ie_t, uint32_t); + BF_FIELD(txwm, 0, 1); + BF_FIELD(rxwm, 1, 1); + END_BF_DECL() r_ie; + + BEGIN_BF_DECL(ip_t, uint32_t); + BF_FIELD(txwm, 0, 1); + BF_FIELD(rxwm, 1, 1); + END_BF_DECL() r_ip; + + // register declarations + scc::sc_register sckdiv; + scc::sc_register sckmode; + scc::sc_register csid; + scc::sc_register csdef; + scc::sc_register csmode; + scc::sc_register delay0; + scc::sc_register delay1; + scc::sc_register fmt; + scc::sc_register txdata; + scc::sc_register rxdata; + scc::sc_register txmark; + scc::sc_register rxmark; + scc::sc_register fctrl; + scc::sc_register ffmt; + scc::sc_register ie; + scc::sc_register ip; + +public: + spi_regs(sc_core::sc_module_name nm); + + template void registerResources(scc::tlm_target &target); +}; +} +////////////////////////////////////////////////////////////////////////////// +// member functions +////////////////////////////////////////////////////////////////////////////// + +inline sysc::spi_regs::spi_regs(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, NAMED(sckdiv, r_sckdiv, 0, *this) +, NAMED(sckmode, r_sckmode, 0, *this) +, NAMED(csid, r_csid, 0, *this) +, NAMED(csdef, r_csdef, 0, *this) +, NAMED(csmode, r_csmode, 0, *this) +, NAMED(delay0, r_delay0, 0, *this) +, NAMED(delay1, r_delay1, 0, *this) +, NAMED(fmt, r_fmt, 0, *this) +, NAMED(txdata, r_txdata, 0, *this) +, NAMED(rxdata, r_rxdata, 0, *this) +, NAMED(txmark, r_txmark, 0, *this) +, NAMED(rxmark, r_rxmark, 0, *this) +, NAMED(fctrl, r_fctrl, 0, *this) +, NAMED(ffmt, r_ffmt, 0, *this) +, NAMED(ie, r_ie, 0, *this) +, NAMED(ip, r_ip, 0, *this) {} + +template inline void sysc::spi_regs::registerResources(scc::tlm_target &target) { + target.addResource(sckdiv, 0x0UL); + target.addResource(sckmode, 0x4UL); + target.addResource(csid, 0x10UL); + target.addResource(csdef, 0x14UL); + target.addResource(csmode, 0x18UL); + target.addResource(delay0, 0x28UL); + target.addResource(delay1, 0x2cUL); + target.addResource(fmt, 0x40UL); + target.addResource(txdata, 0x48UL); + target.addResource(rxdata, 0x4cUL); + target.addResource(txmark, 0x50UL); + target.addResource(rxmark, 0x54UL); + target.addResource(fctrl, 0x60UL); + target.addResource(ffmt, 0x64UL); + target.addResource(ie, 0x70UL); + target.addResource(ip, 0x74UL); +} + +#endif // _SPI_REGS_H_ diff --git a/examples/simple_system/gen/uart_regs.h b/examples/simple_system/gen/uart_regs.h new file mode 100644 index 00000000..e61d300f --- /dev/null +++ b/examples/simple_system/gen/uart_regs.h @@ -0,0 +1,125 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Created on: Wed Sep 20 22:30:45 CEST 2017 +// * uart_regs.h Author: +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _UART_REGS_H_ +#define _UART_REGS_H_ + +#include +#include "scc/register.h" +#include "scc/tlm_target.h" +#include "scc/utilities.h" + +namespace sysc { + +class uart_regs : public sc_core::sc_module, public scc::resetable { +protected: + // storage declarations + BEGIN_BF_DECL(txdata_t, uint32_t); + BF_FIELD(data, 0, 8); + BF_FIELD(full, 31, 1); + END_BF_DECL() r_txdata; + + BEGIN_BF_DECL(rxdata_t, uint32_t); + BF_FIELD(data, 0, 8); + BF_FIELD(empty, 31, 1); + END_BF_DECL() r_rxdata; + + BEGIN_BF_DECL(txctrl_t, uint32_t); + BF_FIELD(txen, 0, 1); + BF_FIELD(nstop, 1, 1); + BF_FIELD(reserved, 2, 14); + BF_FIELD(txcnt, 16, 3); + END_BF_DECL() r_txctrl; + + BEGIN_BF_DECL(rxctrl_t, uint32_t); + BF_FIELD(rxen, 0, 1); + BF_FIELD(reserved, 1, 15); + BF_FIELD(rxcnt, 16, 3); + END_BF_DECL() r_rxctrl; + + BEGIN_BF_DECL(ie_t, uint32_t); + BF_FIELD(txwm, 0, 1); + BF_FIELD(rxwm, 1, 1); + END_BF_DECL() r_ie; + + BEGIN_BF_DECL(ip_t, uint32_t); + BF_FIELD(txwm, 0, 1); + BF_FIELD(rxwm, 1, 1); + END_BF_DECL() r_ip; + + BEGIN_BF_DECL(div_t, uint32_t); + BF_FIELD(div, 0, 16); + END_BF_DECL() r_div; + + // register declarations + scc::sc_register txdata; + scc::sc_register rxdata; + scc::sc_register txctrl; + scc::sc_register rxctrl; + scc::sc_register ie; + scc::sc_register ip; + scc::sc_register div; + +public: + uart_regs(sc_core::sc_module_name nm); + + template void registerResources(scc::tlm_target &target); +}; +} +////////////////////////////////////////////////////////////////////////////// +// member functions +////////////////////////////////////////////////////////////////////////////// + +inline sysc::uart_regs::uart_regs(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, NAMED(txdata, r_txdata, 0, *this) +, NAMED(rxdata, r_rxdata, 0, *this) +, NAMED(txctrl, r_txctrl, 0, *this) +, NAMED(rxctrl, r_rxctrl, 0, *this) +, NAMED(ie, r_ie, 0, *this) +, NAMED(ip, r_ip, 0, *this) +, NAMED(div, r_div, 0, *this) {} + +template inline void sysc::uart_regs::registerResources(scc::tlm_target &target) { + target.addResource(txdata, 0x0UL); + target.addResource(rxdata, 0x4UL); + target.addResource(txctrl, 0x8UL); + target.addResource(rxctrl, 0xcUL); + target.addResource(ie, 0x10UL); + target.addResource(ip, 0x14UL); + target.addResource(div, 0x18UL); +} + +#endif // _UART_REGS_H_ diff --git a/examples/simple_system/gpio.cpp b/examples/simple_system/gpio.cpp new file mode 100644 index 00000000..b6623114 --- /dev/null +++ b/examples/simple_system/gpio.cpp @@ -0,0 +1,220 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial implementation +// +// +//////////////////////////////////////////////////////////////////////////////// + +#include "gpio.h" +#include "scc/report.h" +#include "scc/utilities.h" +#include "gen/gpio_regs.h" +#include + +namespace sysc { + +using namespace sc_core; + +gpio::gpio(sc_module_name nm) +: sc_module(nm) +, tlm_target<>(clk) +, NAMED(clk_i) +, NAMED(rst_i) +, NAMED(pins_o, 32) +, NAMED(pins_i, 32) +, NAMED(iof0_o, 32) +, NAMED(iof1_o, 32) +, NAMED(iof0_i, 32) +, NAMED(iof1_i, 32) +, NAMEDD(regs, gpio_regs) +{ + regs->registerResources(*this); + SC_METHOD(clock_cb); + sensitive << clk_i; + SC_METHOD(reset_cb); + sensitive << rst_i; + dont_initialize(); + auto pins_i_cb =[this](unsigned int tag, tlm::scc::tlm_signal_gp<>& gp, + tlm::tlm_phase& phase, sc_time& delay)->tlm::tlm_sync_enum{ + this->pin_input(tag, gp, delay); + return tlm::TLM_COMPLETED; + }; + auto i=0U; + for(auto& s:pins_i){ + s.register_nb_transport(pins_i_cb, i); + ++i; + } + auto iof0_i_cb =[this](unsigned int tag, tlm::scc::tlm_signal_gp<>& gp, + tlm::tlm_phase& phase, sc_time& delay)->tlm::tlm_sync_enum{ + last_iof0[tag]=gp.get_value(); + this->iof_input(tag, 0, gp, delay); + return tlm::TLM_COMPLETED; + }; + i=0; + for(auto& s:iof0_i){ + s.register_nb_transport(iof0_i_cb, i); + ++i; + } + auto iof1_i_cb =[this](unsigned int tag, tlm::scc::tlm_signal_gp<>& gp, + tlm::tlm_phase& phase, sc_time& delay)->tlm::tlm_sync_enum{ + last_iof1[tag]=gp.get_value(); + this->iof_input(tag, 1, gp, delay); + return tlm::TLM_COMPLETED; + }; + i=0; + for(auto& s:iof1_i){ + s.register_nb_transport(iof1_i_cb, i); + ++i; + } + auto update_pins_cb = [this](scc::sc_register ®, uint32_t data, sc_time d) -> bool { + if (!this->regs->in_reset()) { + auto changed_bits = (reg.get()^data); + reg.put(data); + update_pins(changed_bits); + } + return true; + }; + regs->port.set_write_cb(update_pins_cb); + regs->output_en.set_write_cb(update_pins_cb); + regs->out_xor.set_write_cb(update_pins_cb); + regs->iof_en.set_write_cb(update_pins_cb); + regs->iof_sel.set_write_cb(update_pins_cb); +} + +gpio::~gpio() = default; + +void gpio::reset_cb() { + if (rst_i.read()){ + regs->reset_start(); + } else { + regs->reset_stop(); + } + update_pins(std::numeric_limits::max()); +} + +void gpio::clock_cb() { + this->clk = clk_i.read(); +} + +tlm::tlm_phase gpio::write_output(tlm::scc::tlm_signal_gp& gp, size_t i, bool val) { + sc_time delay{SC_ZERO_TIME}; + tlm::tlm_phase phase{ tlm::BEGIN_REQ }; + gp.set_command(tlm::TLM_WRITE_COMMAND); + gp.set_response_status(tlm::TLM_OK_RESPONSE); + gp.set_value(val); + pins_o.at(i)->nb_transport_fw(gp, phase, delay); + return phase; +} + +void gpio::update_pins(uint32_t changed_bits) { + sc_inout_rv<32>::data_type out_val; + tlm::scc::tlm_signal_gp gp; + bool val{false}; + for(size_t i=0, mask = 1; i<32; ++i, mask<<=1){ + if(changed_bits&mask){ + if(((regs->r_iof_en&mask)!=0) && (iof0_i[i].size()==0 || iof1_i[i].size()==0)){ + if((regs->r_iof_sel&mask)==0 && iof0_i[i].size()>0){ + val=last_iof0[i]?sc_dt::Log_1:sc_dt::Log_0; + } else if((regs->r_iof_sel&mask)==1 && iof1_i[i].size()>0) + val=last_iof1[i]?sc_dt::Log_1:sc_dt::Log_0; + } else { + if((regs->r_output_en&mask) && (regs->r_port&mask)) + val=true; + else + val=false; + if(regs->r_out_xor&mask) + val=~val; + } + tlm::tlm_phase phase = write_output(gp, i, val); + } + } +} + +void gpio::pin_input(unsigned int tag, tlm::scc::tlm_signal_gp& gp, sc_time& delay) { + if(delay>SC_ZERO_TIME){ + wait(delay); + delay=SC_ZERO_TIME; + } + auto mask = 1u<r_output_en&mask)==0){ + if(gp.get_value()) + regs->r_value|=mask; + else + regs->r_value&=~mask; + forward_pin_input(tag, gp); + } +} + +void gpio::forward_pin_input(unsigned int tag, tlm::scc::tlm_signal_gp& gp) { + const auto mask = 1U<iof_en&mask){ + auto& socket = regs->iof_sel&mask?iof1_o[tag]:iof0_o[tag]; + tlm::scc::tlm_signal_gp<> new_gp; + for(size_t i=0; inb_transport_fw(new_gp, phase, delay); // we don't care about phase and sync enum + } + } +} + +void gpio::iof_input(unsigned int tag, unsigned iof_idx, tlm::scc::tlm_signal_gp<>& gp, sc_time& delay) { + if(delay>SC_ZERO_TIME){ + wait(delay); + delay=SC_ZERO_TIME; + } + const auto mask = 1U<r_iof_en&mask){ + const auto idx = regs->r_iof_sel&mask?1:0; + if(iof_idx == idx){ + auto& socket = pins_o[tag]; + for(size_t i=0; i new_gp; + new_gp.set_command(tlm::TLM_WRITE_COMMAND); + auto val = gp.get_value(); + new_gp.set_value(val); + new_gp.copy_extensions_from(gp); + socket->nb_transport_fw(new_gp, phase, delay); // we don't care about phase and sync enum + gp.update_extensions_from(new_gp); + } + } + } +} + +} /* namespace sysc */ + diff --git a/examples/simple_system/gpio.h b/examples/simple_system/gpio.h new file mode 100644 index 00000000..3c1c97e8 --- /dev/null +++ b/examples/simple_system/gpio.h @@ -0,0 +1,86 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial implementation +// +// +//////////////////////////////////////////////////////////////////////////////// + +#ifndef _GPIO_H_ +#define _GPIO_H_ + +#include "scc/tlm_target.h" +#include "tlm/scc/signal_target_mixin.h" +#include "tlm/scc/signal_initiator_mixin.h" +#include + +namespace sysc { + +class gpio_regs; +class WsHandler; + +class gpio : public sc_core::sc_module, public scc::tlm_target<> { +public: + SC_HAS_PROCESS(gpio); + sc_core::sc_in clk_i; + sc_core::sc_in rst_i; + // sc_core::sc_inout_rv<32> pins_io; + + sc_core::sc_vector pins_o; + sc_core::sc_vector pins_i; + + sc_core::sc_vector iof0_o; + sc_core::sc_vector iof1_o; + sc_core::sc_vector iof0_i; + sc_core::sc_vector iof1_i; + + gpio(sc_core::sc_module_name nm); + virtual ~gpio() override; // need to keep it in source file because of fwd declaration of gpio_regs + +protected: + void clock_cb(); + void reset_cb(); + void update_pins(uint32_t changed_bits); + void pin_input(unsigned int tag, tlm::scc::tlm_signal_gp<>& gp, sc_core::sc_time& delay); + void forward_pin_input(unsigned int tag, tlm::scc::tlm_signal_gp<>& gp); + void iof_input(unsigned int tag, unsigned iof_idx, tlm::scc::tlm_signal_gp<>& gp, sc_core::sc_time& delay); + sc_core::sc_time clk; + std::array last_iof0, last_iof1; + std::unique_ptr regs; + std::shared_ptr handler; + +private: + tlm::tlm_phase write_output(tlm::scc::tlm_signal_gp<>& gp, size_t i, bool val); +}; + +} /* namespace sysc */ + +#endif /* _GPIO_H_ */ diff --git a/examples/simple_system/plic.cpp b/examples/simple_system/plic.cpp new file mode 100644 index 00000000..3b4cbac8 --- /dev/null +++ b/examples/simple_system/plic.cpp @@ -0,0 +1,177 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017, MINRES Technologies GmbH +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY0x200004, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Contributors: +// eyck@minres.com - initial API and implementation +// +// +//////////////////////////////////////////////////////////////////////////////// + +// todo: truncate values beyond 7 (in prio_threshold write_cb) + +#include "plic.h" +#include "gen/plic_regs.h" +#include "scc/utilities.h" +#include + +namespace sysc { + +plic::plic(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, tlm_target<>(clk) +, NAMED(clk_i) +, NAMED(rst_i) +, NAMED(global_interrupts_i, 256) +, NAMED(core_interrupt_o) +, NAMEDD(regs, plic_regs) +{ + regs->registerResources(*this); + // register callbacks + init_callbacks(); + regs->claim_complete.set_write_cb(m_claim_complete_write_cb); + + // port callbacks + SC_METHOD(global_int_port_cb); + for (uint8_t i = 0; i < 255; i++) { + sensitive << global_interrupts_i[i].pos(); + } + dont_initialize(); + + // register event callbacks + SC_METHOD(clock_cb); + sensitive << clk_i; + SC_METHOD(reset_cb); + sensitive << rst_i; +} + +plic::~plic() = default; + +void plic::init_callbacks() { + m_claim_complete_write_cb = [=](scc::sc_register reg, uint32_t v) -> bool { + reg.put(v); + reset_pending_int(v); + // std::cout << "Value of register: 0x" << std::hex << reg << std::endl; + // todo: reset related interrupt and find next high-prio interrupt + return true; + }; +} + +void plic::clock_cb() { this->clk = clk_i.read(); } + +void plic::reset_cb() { + if (rst_i.read()) + regs->reset_start(); + else + regs->reset_stop(); +} + +// Functional handling of interrupts: +// - global_int_port_cb() +// - set pending register bits +// - called by: incoming global_int +// - handle_pending_int() +// - update claim register content +// - generate core-interrupt pulse +// - called by: +// - incoming global_int +// - complete-register write access +// - reset_pending_int(int-id) +// - reset pending bit +// - call next handle_pending_int() +// - called by: +// - complete-reg write register content + +void plic::global_int_port_cb() { + + // set related pending bit if enable is set for incoming global_interrupt + + // todo: extend up to 255 bits (limited to 32 right now) + for (uint32_t i = 1; i < 32; i++) { + uint32_t enable_bits = regs->r_enabled; + bool enable = enable_bits & (0x1 << i); // read enable bit + + if (enable && global_interrupts_i[i].read() == 1) { + regs->r_pending = regs->r_pending | (0x1 << i); + SCCDEBUG("plic") << "pending interrupt identified: " << i; + } + } + + handle_pending_int(); +} + +void plic::handle_pending_int() { + // identify high-prio pending interrupt and raise a core-interrupt + uint32_t claim_int = 0; // claim interrupt + uint32_t claim_prio = 0; // related priority (highest prio interrupt wins the race) + bool raise_int = false; + uint32_t thold = regs->r_threshold.threshold; // threshold value + + // todo: extend up to 255 bits (limited to 32 right now) + for (uint32_t i = 1; i < 32; i++) { + uint32_t pending_bits = regs->r_pending; + bool pending = (pending_bits & (0x1 << i)) ? true : false; + uint32_t prio = regs->r_priority[i - 1].priority; // read priority value + + if (pending && thold < prio) { + regs->r_pending = regs->r_pending | (0x1 << i); + // below condition ensures implicitly that lowest id is selected in case of multiple identical + // priority-interrupts + if (prio > claim_prio) { + claim_prio = prio; + claim_int = i; + raise_int = true; + SCCDEBUG("plic") << "pending interrupt activated: " << i; + } + } + } + + if (raise_int) { + regs->r_claim_complete = claim_int; + core_interrupt_o.write(true); + // todo: evluate clock period + } else { + regs->r_claim_complete = 0; + SCCDEBUG("plic") << "no further pending interrupt."; + } +} + +void plic::reset_pending_int(uint32_t irq) { + // todo: evaluate enable register (see spec) + // todo: make sure that pending is set, otherwise don't reset irq ... read spec. + SCCDEBUG("plic") << "reset pending interrupt: " << irq; + // reset related pending bit + regs->r_pending &= ~(0x1 << irq); + core_interrupt_o.write(false); + + // evaluate next pending interrupt + handle_pending_int(); +} + +} /* namespace sysc */ diff --git a/examples/simple_system/plic.h b/examples/simple_system/plic.h new file mode 100644 index 00000000..0cb60b5a --- /dev/null +++ b/examples/simple_system/plic.h @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright 2017 eyck@minres.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ + +#ifndef _PLIC_H_ +#define _PLIC_H_ + +#include +#include + +namespace sysc { + +class plic_regs; + +class plic : public sc_core::sc_module, public scc::tlm_target<> { +public: + SC_HAS_PROCESS(plic); + sc_core::sc_in clk_i; + sc_core::sc_in rst_i; + sc_core::sc_vector> global_interrupts_i; + sc_core::sc_out core_interrupt_o; + sc_core::sc_event raise_int_ev; + sc_core::sc_event clear_int_ev; + plic(sc_core::sc_module_name nm); + virtual ~plic(); + +protected: + void clock_cb(); + void reset_cb(); + void init_callbacks(); + + void global_int_port_cb(); + void handle_pending_int(); + void reset_pending_int(uint32_t irq); + + void raise_core_interrupt(); + void clear_core_interrupt(); + sc_core::sc_time clk; + std::unique_ptr regs; + std::function, uint32_t)> m_claim_complete_write_cb; +}; + +} /* namespace sysc */ + +#endif /* _PLIC_H_ */ diff --git a/examples/simple_system/sc_main.cpp b/examples/simple_system/sc_main.cpp new file mode 100644 index 00000000..131b19f1 --- /dev/null +++ b/examples/simple_system/sc_main.cpp @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 eyck@minres.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +//////////////////////////////////////////////////////////////////////////////// +/* + * sc_main.cpp + * + * Created on: 17.09.2017 + * Author: eyck@minres.com + */ + +#include "simple_system.h" +#include +#include +#include + +using namespace sysc; +using namespace scc; +namespace po = boost::program_options; + +namespace { +const size_t ERROR_IN_COMMAND_LINE = 1; +const size_t SUCCESS = 0; +const size_t ERROR_UNHANDLED_EXCEPTION = 2; +} // namespace + +int sc_main(int argc, char *argv[]) { + sc_core::sc_report_handler::set_actions( "/IEEE_Std_1666/deprecated", sc_core::SC_DO_NOTHING ); + sc_core::sc_report_handler::set_actions(sc_core::SC_ID_MORE_THAN_ONE_SIGNAL_DRIVER_, sc_core::SC_DO_NOTHING); + /////////////////////////////////////////////////////////////////////////// + // CLI argument parsing + /////////////////////////////////////////////////////////////////////////// + po::options_description desc("Options"); + // clang-format off + desc.add_options() + ("help,h", "Print help message") + ("debug,d", "set debug level") + ("trace,t", "trace SystemC signals"); + // clang-format on + po::variables_map vm; + try { + po::store(po::parse_command_line(argc, argv, desc), vm); // can throw + // --help option + if (vm.count("help")) { + std::cout << "JIT-ISS simulator for AVR" << std::endl << desc << std::endl; + return SUCCESS; + } + po::notify(vm); // throws on error, so do after help in case + // there are any problems + } catch (po::error &e) { + std::cerr << "ERROR: " << e.what() << std::endl << std::endl; + std::cerr << desc << std::endl; + return ERROR_IN_COMMAND_LINE; + } + /////////////////////////////////////////////////////////////////////////// + // configure logging + /////////////////////////////////////////////////////////////////////////// + scc::init_logging(vm.count("debug")?scc::log::DEBUG:scc::log::INFO); + /////////////////////////////////////////////////////////////////////////// + // set up tracing & transaction recording + /////////////////////////////////////////////////////////////////////////// + tracer trace("simple_system", tracer::COMPRESSED, vm.count("trace")); + // todo: fix displayed clock period in VCD + + /////////////////////////////////////////////////////////////////////////// + // instantiate top level + /////////////////////////////////////////////////////////////////////////// + simple_system i_simple_system("i_simple_system"); + + /////////////////////////////////////////////////////////////////////////// + // run simulation + /////////////////////////////////////////////////////////////////////////// + sc_start(sc_core::sc_time(1, sc_core::SC_MS)); + // todo: provide end-of-simulation macros + + if (!sc_core::sc_end_of_simulation_invoked()) { + SCCERR() << "simulation timed out"; + sc_core::sc_stop(); + } + return SUCCESS; +} diff --git a/examples/simple_system/simple_system.cpp b/examples/simple_system/simple_system.cpp new file mode 100644 index 00000000..852d33f3 --- /dev/null +++ b/examples/simple_system/simple_system.cpp @@ -0,0 +1,87 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 eyck@minres.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +//////////////////////////////////////////////////////////////////////////////// +/* + * simplesystem.cpp + * + * Created on: 17.09.2017 + * Author: eyck@minres.com + */ + +#include "simple_system.h" + +namespace sysc { + +simple_system::simple_system(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, NAMED(i_master) +, NAMED(i_router, 4, 1) +, NAMED(i_uart) +, NAMED(i_spi) +, NAMED(i_gpio) +, NAMED(i_plic) +, NAMED(s_clk) +, NAMED(s_rst) +, NAMED(s_global_interrupts, 256) +, NAMED(s_core_interrupt) +, NAMED(s_gpio, 32) +{ + // todo: discuss naming conventions (s_ vs. _i/_o) --> covnert into _s + + // bus connections + i_master.intor(i_router.target[0]); + i_router.bind_target(i_plic.socket, 0, "plic"); + i_router.bind_target(i_uart.socket, 1, "uart"); + i_router.bind_target(i_spi.socket, 2, "spi"); + i_router.bind_target(i_gpio.socket, 3, "gpio"); + + // target address ranges + for (const auto &e : e300_plat_map) + i_router.add_target_range(e.name, e.start, e.size); + + // clock/reset connections + i_uart.clk_i(s_clk); + i_spi.clk_i(s_clk); + i_gpio.clk_i(s_clk); + i_plic.clk_i(s_clk); + s_clk.write(10_ns); + + i_uart.rst_i(s_rst); + i_spi.rst_i(s_rst); + i_gpio.rst_i(s_rst); + i_plic.rst_i(s_rst); + i_master.rst_i(s_rst); + + // interrupt connections + i_plic.core_interrupt_o(s_core_interrupt); + i_plic.global_interrupts_i.bind(s_global_interrupts); + i_master.global_interrupts_o(s_global_interrupts); + i_master.core_interrupt_i(s_core_interrupt); + + for(auto i=0U; i +#include +#include + +namespace sysc { + +class simple_system : public sc_core::sc_module { +public: + SC_HAS_PROCESS(simple_system); + + test_initiator i_master; + scc::router<> i_router; + uart i_uart; + spi i_spi; + gpio i_gpio; + plic i_plic; + sc_core::sc_signal s_clk; + sc_core::sc_signal s_rst; + sc_core::sc_vector> s_global_interrupts; + sc_core::sc_signal s_core_interrupt; + sc_core::sc_vector> s_gpio; + + simple_system(sc_core::sc_module_name nm); + +protected: + void gen_reset(); + +#include "gen/e300_plat_t.h" +}; + +} /* namespace sysc */ + +#endif /* SIMPLESYSTEM_H_ */ diff --git a/examples/simple_system/spi.cpp b/examples/simple_system/spi.cpp new file mode 100644 index 00000000..94e0e5c8 --- /dev/null +++ b/examples/simple_system/spi.cpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 eyck@minres.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +//////////////////////////////////////////////////////////////////////////////// + +#include "spi.h" +#include "gen/spi_regs.h" +#include "scc/utilities.h" + +namespace sysc { + +spi::spi(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, tlm_target<>(clk) +, NAMED(clk_i) +, NAMED(rst_i) +, NAMEDD(regs, spi_regs) { + regs->registerResources(*this); + SC_METHOD(clock_cb); + sensitive << clk_i; + SC_METHOD(reset_cb); + sensitive << rst_i; +} + +spi::~spi() {} // NOLINT + +void spi::clock_cb() { this->clk = clk_i.read(); } + +void spi::reset_cb() { + if (rst_i.read()) + regs->reset_start(); + else + regs->reset_stop(); +} + +} /* namespace sysc */ diff --git a/examples/simple_system/spi.h b/examples/simple_system/spi.h new file mode 100644 index 00000000..b97fbf82 --- /dev/null +++ b/examples/simple_system/spi.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright 2017 eyck@minres.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ + +#ifndef _SPI_H_ +#define _SPI_H_ + +#include + +namespace sysc { + +class spi_regs; + +class spi : public sc_core::sc_module, public scc::tlm_target<> { +public: + SC_HAS_PROCESS(spi); + sc_core::sc_in clk_i; + sc_core::sc_in rst_i; + spi(sc_core::sc_module_name nm); + virtual ~spi(); + +protected: + void clock_cb(); + void reset_cb(); + sc_core::sc_time clk; + std::unique_ptr regs; +}; + +} /* namespace sysc */ + +#endif /* _SPI_H_ */ diff --git a/examples/simple_system/test_initiator.cpp b/examples/simple_system/test_initiator.cpp new file mode 100644 index 00000000..c2effdef --- /dev/null +++ b/examples/simple_system/test_initiator.cpp @@ -0,0 +1,272 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 eyck@minres.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +//////////////////////////////////////////////////////////////////////////////// +/* + * test_initiator.cpp + * + * Created on: 17.09.2017 + * Author: eyck@minres.com + */ + +#include "test_initiator.h" +#include +#include +#include + +// todo: move into gen folder somewhere (adapt code-generator) +#define PLIC_PRIO1_REG 0x0C000004 +#define PLIC_PRIO2_REG 0x0C000008 +#define PLIC_PRIO3_REG 0x0C00000C +#define PLIC_PRIO4_REG 0x0C000010 +#define PLIC_PENDING_REG 0x0C001000 +#define PLIC_ENABLE_REG 0x0C002000 +#define PLIC_PRIO_TRESHOLD_REG 0x0C200000 +#define PLIC_CLAIM_COMPLETE_REG 0x0C200004 + +namespace sysc { +using namespace sc_core; + +test_initiator::test_initiator(sc_module_name nm) +: sc_module(nm) +, NAMED(intor) +, NAMED(rst_i) +, NAMED(global_interrupts_o, 256) +, NAMED(core_interrupt_i) { + SC_THREAD(run); + + SC_METHOD(core_irq_handler); + sensitive << core_interrupt_i; + dont_initialize(); +} + +void test_initiator::run() { + // wait for reset + if (rst_i.read() == false) wait(rst_i.posedge_event()); + wait(rst_i.negedge_event()); + wait(10_ns); + + // apply test-sequences + test_unique_irq(); + test_frequent_irq(); + test_parallel_irq(); + test_irq_stress(); + + // todo: review irq sequences from FW point of view ... expected ??? + wait(100_ns); + sc_stop(); +} + +void test_initiator::test_unique_irq() { + + //// enable reg is not set + // -> irq to be ignored + // -> no core_interrupt + // -> no entry in pending reg + + // generate interrupt pulse (note: 1 is lowest usable register) + global_interrupts_o[2].write(true); + wait(10_ns); + global_interrupts_o[2].write(false); + wait(10_ns); + + reg_check(PLIC_PENDING_REG, 0x0); + wait(10_ns); + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x0); + wait(10_ns); + + //// enable reg is set, then + // -> pending bit change expected + // -> core_interrupt expected + + read_bus(PLIC_PRIO1_REG); + wait(10_ns); + + // enable single interrupt + write_bus(PLIC_PRIO1_REG, 0x1); + wait(10_ns); + + write_bus(PLIC_ENABLE_REG, 0x2); + wait(10_ns); + + // generate interrupt pulse (note: 1 is lowest usable register) + global_interrupts_o[1].write(true); + wait(10_ns); + global_interrupts_o[1].write(false); + wait(10_ns); + + // read claim_complete register + reg_check(PLIC_PENDING_REG, 0x2); + wait(10_ns); + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x1); + wait(10_ns); + + //// after writing to claim_complete reg (per fw) + // -> pending bit expected to be unset + // -> enable bit expected to be set ... test with / without enable being set + write_bus(PLIC_CLAIM_COMPLETE_REG, 0x1); + wait(10_ns); + reg_check(PLIC_PENDING_REG, 0x0); + wait(10_ns); + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x0); + wait(10_ns); + + // todo: remove wait statements once the tlm_initiator is in place + // todo: evaluate error messages ... provide correct pass/fail verdict + + wait(100_ns); +} + +void test_initiator::test_frequent_irq() {} + +void test_initiator::test_parallel_irq() { + + //// create three parallel global_int requests + // -> read and clear bits one after the other + // -> different priorities applied (reverse order) + // -> correct priority handing expected + // -> three core interrupts expected in total + + // reverse order priority configuration + write_bus(PLIC_PRIO1_REG, 0x3); + wait(10_ns); + write_bus(PLIC_PRIO2_REG, 0x2); + wait(10_ns); + write_bus(PLIC_PRIO3_REG, 0x1); + wait(10_ns); + + // enable all three interrupts + write_bus(PLIC_ENABLE_REG, 0xE); + wait(10_ns); + + // generate interrupt pulse (note: 1 is lowest usable register) + global_interrupts_o[1].write(true); + wait(10_ns); + global_interrupts_o[1].write(false); + wait(10_ns); + global_interrupts_o[2].write(true); + wait(10_ns); + global_interrupts_o[2].write(false); + wait(10_ns); + global_interrupts_o[3].write(true); + wait(10_ns); + global_interrupts_o[3].write(false); + wait(10_ns); + + // expect three pending registers + reg_check(PLIC_PENDING_REG, 0xE); + wait(10_ns); + + // expect lowest interrupt id to be highest int + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x1); + wait(10_ns); + + //// after writing to claim_complete reg (per fw) + // -> next int to become highest irq + write_bus(PLIC_CLAIM_COMPLETE_REG, 0x1); + wait(10_ns); + reg_check(PLIC_PENDING_REG, 0xC); + wait(10_ns); + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x2); + wait(10_ns); + + //// after writing to claim_complete reg again (per fw) + // -> next int to become highest irq + write_bus(PLIC_CLAIM_COMPLETE_REG, 0x2); + wait(10_ns); + reg_check(PLIC_PENDING_REG, 0x8); + wait(10_ns); + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x3); + wait(10_ns); + + //// after last writing to claim_complete reg again (per fw) + // -> no further pending irq expected + write_bus(PLIC_CLAIM_COMPLETE_REG, 0x3); + wait(10_ns); + reg_check(PLIC_PENDING_REG, 0x0); + wait(10_ns); + reg_check(PLIC_CLAIM_COMPLETE_REG, 0x0); + wait(10_ns); + + // todo: advance upon register-write access ... remove above 10_ns waits + // todo: evaluate error messages ... provide correct pass/fail verdict + + wait(100_ns); +} + +void test_initiator::test_irq_stress() {} + +void test_initiator::write_bus(std::uint32_t adr, std::uint32_t dat) { + tlm::tlm_generic_payload gp; + std::array data; + data[3] = 0xff & dat >> 24; + data[2] = 0xff & dat >> 16; + data[1] = 0xff & dat >> 8; + data[0] = 0xff & dat; + + SCCDEBUG("test_initiator") << "write_bus(0x" << std::hex << adr << ") : " << dat; + + gp.set_command(tlm::TLM_WRITE_COMMAND); + gp.set_address(adr); + gp.set_data_ptr(data.data()); + gp.set_data_length(data.size()); + gp.set_streaming_width(4); + sc_time delay; + intor->b_transport(gp, delay); + + if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) { + throw std::exception(); + } +} + +std::uint32_t test_initiator::read_bus(std::uint32_t adr) { + + tlm::tlm_generic_payload gp; + std::array data; + + gp.set_command(tlm::TLM_READ_COMMAND); + gp.set_address(adr); + gp.set_data_ptr(data.data()); + gp.set_data_length(data.size()); + gp.set_streaming_width(4); + sc_time delay; + intor->b_transport(gp, delay); + + if (gp.get_response_status() != tlm::TLM_OK_RESPONSE) { + // todo: improve output in case of exception, define own exception class to carry transaction-infos + // ... i.e. out-of-range report with info about legal mem boundaries + throw std::exception(); + } + + // todo: use reinterpret_cast instead + std::uint32_t rdat = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; + + SCCDEBUG("test_initiator") << "read_bus(0x" << std::hex << adr << ") -> " << rdat; + return rdat; +} + +void test_initiator::reg_check(std::uint32_t adr, std::uint32_t exp) { + uint32_t dat = read_bus(adr); + if (dat != exp) { + SCCERR("test_initiator") << "register check failed for address 0x" << std::hex << adr << ": " << dat << " != " << exp; + } else { + SCCDEBUG("test_initiator") << "register check passed for address 0x" << std::hex << adr << ": " << dat; + } +} + +void test_initiator::core_irq_handler() { + SCCDEBUG("test_initiator") << "core_interrupt_i edge detected -> " << core_interrupt_i.read(); +} + +} /* namespace sysc */ diff --git a/examples/simple_system/test_initiator.h b/examples/simple_system/test_initiator.h new file mode 100644 index 00000000..ee69ace8 --- /dev/null +++ b/examples/simple_system/test_initiator.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright 2017 eyck@minres.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ +/* + * test_initiator.h + * + * Created on: 17.09.2017 + * Author: eyck@minres.com + */ + +#ifndef _TEST_INITIATOR_H_ +#define _TEST_INITIATOR_H_ + +#include +#include + +namespace sysc { + +class test_initiator : public sc_core::sc_module { +public: + SC_HAS_PROCESS(test_initiator); + tlm_utils::simple_initiator_socket intor; + sc_core::sc_vector> global_interrupts_o; + sc_core::sc_in core_interrupt_i; + sc_core::sc_in rst_i; + test_initiator(sc_core::sc_module_name nm); + +protected: + void run(); + void test_unique_irq(); + void test_frequent_irq(); + void test_parallel_irq(); + void test_irq_stress(); + void write_bus(std::uint32_t adr, std::uint32_t dat); + std::uint32_t read_bus(std::uint32_t adr); + void reg_check(std::uint32_t adr, std::uint32_t exp); + + void core_irq_handler(); +}; + +} /* namespace sysc */ + +#endif /* _TEST_INITIATOR_H_ */ diff --git a/examples/simple_system/uart.cpp b/examples/simple_system/uart.cpp new file mode 100644 index 00000000..e3f79952 --- /dev/null +++ b/examples/simple_system/uart.cpp @@ -0,0 +1,47 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 eyck@minres.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +//////////////////////////////////////////////////////////////////////////////// + +#include "uart.h" +#include "gen/uart_regs.h" +#include "scc/utilities.h" + +namespace sysc { + +uart::uart(sc_core::sc_module_name nm) +: sc_core::sc_module(nm) +, tlm_target<>(clk) +, NAMED(clk_i) +, NAMED(rst_i) +, NAMEDD(regs, uart_regs) { + regs->registerResources(*this); + SC_METHOD(clock_cb); + sensitive << clk_i; + SC_METHOD(reset_cb); + sensitive << rst_i; +} + +uart::~uart() {} // NOLINT + +void uart::clock_cb() { this->clk = clk_i.read(); } + +void uart::reset_cb() { + if (rst_i.read()) + regs->reset_start(); + else + regs->reset_stop(); +} + +} /* namespace sysc */ diff --git a/examples/simple_system/uart.h b/examples/simple_system/uart.h new file mode 100644 index 00000000..3725003a --- /dev/null +++ b/examples/simple_system/uart.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright 2017 eyck@minres.com + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + ******************************************************************************/ + +#ifndef _UART_H_ +#define _UART_H_ + +#include + +namespace sysc { + +class uart_regs; + +class uart : public sc_core::sc_module, public scc::tlm_target<> { +public: + SC_HAS_PROCESS(uart); + sc_core::sc_in clk_i; + sc_core::sc_in rst_i; + uart(sc_core::sc_module_name nm); + virtual ~uart(); + +protected: + void clock_cb(); + void reset_cb(); + sc_core::sc_time clk; + std::unique_ptr regs; +}; + +} /* namespace sysc */ + +#endif /* _UART_H_ */ diff --git a/examples/transaction_recording/CMakeLists.txt b/examples/transaction_recording/CMakeLists.txt new file mode 100644 index 00000000..42f8db77 --- /dev/null +++ b/examples/transaction_recording/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.12) +add_executable (transaction_recording + scv_tr_recording_example.cpp +) +target_link_libraries (transaction_recording LINK_PUBLIC scc) + +add_test(NAME tx_rec_test COMMAND transaction_recording) \ No newline at end of file diff --git a/examples/transaction_recording/scv_tr_recording_example.cpp b/examples/transaction_recording/scv_tr_recording_example.cpp new file mode 100644 index 00000000..7535acc4 --- /dev/null +++ b/examples/transaction_recording/scv_tr_recording_example.cpp @@ -0,0 +1,406 @@ +// -*- C++ -*- +/***************************************************************************** + + The following code is derived, directly or indirectly, from the SystemC + source code Copyright (c) 1996-2014 by all Contributors. + All Rights reserved. + + The contents of this file are subject to the restrictions and limitations + set forth in the SystemC Open Source License (the "License"); + You may not use this file except in compliance with such restrictions and + limitations. You may obtain instructions on how to receive a copy of the + License at http://www.accellera.org/. Software distributed by Contributors + under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF + ANY KIND, either express or implied. See the License for the specific + language governing rights and limitations under the License. + + *****************************************************************************/ +#include "scv.h" +#include "scc/scv_tr_db.h" +#include "scc/report.h" +#include "scc/value_registry.h" +#include + +// text 11308µs/11602µs +// compressed 10365µs/ 9860µs +// binary 13233µs/10698µs +// SQLite 30363µs/30018µs +// LeveDB 23898µs/22367µs + +// hack to fake a true fifo_mutex +#define fifo_mutex sc_mutex + +const unsigned ram_size = 256; + +class rw_task_if : virtual public sc_interface { +public: + using addr_t = sc_uint<8>; + using data_t = sc_uint<8>; + struct write_t { + addr_t addr; + data_t data; + }; + + virtual data_t read(const addr_t *) = 0; + virtual void write(const write_t *) = 0; +}; + +SCV_EXTENSIONS(rw_task_if::write_t) { +public: + scv_extensions addr; + scv_extensions data; + SCV_EXTENSIONS_CTOR(rw_task_if::write_t) { + SCV_FIELD(addr); + SCV_FIELD(data); + } +}; + +class pipelined_bus_ports : public sc_module { +public: + sc_in clk; + sc_inout rw; + sc_inout addr_req; + sc_inout addr_ack; + sc_inout> bus_addr; + sc_inout data_rdy; + sc_inout> bus_data; + + SC_CTOR(pipelined_bus_ports) + : clk("clk") + , rw("rw") + , addr_req("addr_req") + , addr_ack("addr_ack") + , bus_addr("bus_addr") + , data_rdy("data_rdy") + , bus_data("bus_data") {} + void trace(sc_trace_file *tf) const override; +}; + +void pipelined_bus_ports::trace(sc_trace_file *tf) const { + sc_trace(tf, clk, clk.name()); + sc_trace(tf, rw, rw.name()); + sc_trace(tf, addr_req, addr_req.name()); + sc_trace(tf, addr_ack, addr_ack.name()); + sc_trace(tf, bus_addr, bus_addr.name()); + sc_trace(tf, data_rdy, data_rdy.name()); + sc_trace(tf, bus_data, bus_data.name()); +} + +class rw_pipelined_transactor : public rw_task_if, public pipelined_bus_ports { + + fifo_mutex addr_phase; + fifo_mutex data_phase; + + scv_tr_stream pipelined_stream; + scv_tr_stream addr_stream; + scv_tr_stream data_stream; + scv_tr_generator, sc_uint<8>> read_gen; + scv_tr_generator, sc_uint<8>> write_gen; + scv_tr_generator> addr_gen; + scv_tr_generator<_scv_tr_generator_default_data, sc_uint<8>> rdata_gen; + scv_tr_generator> wdata_gen; + +public: + rw_pipelined_transactor(sc_module_name nm) + : pipelined_bus_ports(nm) + , addr_phase("addr_phase") + , data_phase("data_phase") + , pipelined_stream((std::string(name()) + ".pipelined_stream").c_str(), "transactor") + , addr_stream((std::string(name()) + ".addr_stream").c_str(), "transactor") + , data_stream((std::string(name()) + ".data_stream").c_str(), "transactor") + , read_gen("read", pipelined_stream, "addr", "data") + , write_gen("write", pipelined_stream, "addr", "data") + , addr_gen("addr", addr_stream, "addr") + , rdata_gen("rdata", data_stream, nullptr, "data") + , wdata_gen("wdata", data_stream, "data") {} + data_t read(const addr_t *p_addr) override; + void write(const write_t *req) override; +}; + +rw_task_if::data_t rw_pipelined_transactor::read(const addr_t *addr) { + addr_phase.lock(); + scv_tr_handle h = read_gen.begin_transaction(*addr); + h.record_attribute("data_size", sizeof(data_t)); + scv_tr_handle h1 = addr_gen.begin_transaction(*addr, "addr_phase", h); + wait(clk->posedge_event()); + bus_addr = *addr; + rw = false; + addr_req = true; + wait(addr_ack->posedge_event()); + wait(clk->negedge_event()); + addr_req = false; + wait(addr_ack->negedge_event()); + addr_gen.end_transaction(h1); + addr_phase.unlock(); + + data_phase.lock(); + scv_tr_handle h2 = rdata_gen.begin_transaction("data_phase", h); + wait(data_rdy->posedge_event()); + data_t data = bus_data.read(); + wait(data_rdy->negedge_event()); + rdata_gen.end_transaction(h2, data); + read_gen.end_transaction(h, data); + data_phase.unlock(); + + return data; +} + +void rw_pipelined_transactor::write(const write_t *req) { + addr_phase.lock(); + scv_tr_handle h = write_gen.begin_transaction(req->addr); + h.record_attribute("data_size", sizeof(data_t)); + scv_tr_handle h1 = addr_gen.begin_transaction(req->addr, "addr_phase", h); + wait(clk->posedge_event()); + bus_addr = req->addr; + rw = true; + addr_req = true; + wait(addr_ack->posedge_event()); + wait(clk->negedge_event()); + addr_req = false; + wait(addr_ack->negedge_event()); + addr_gen.end_transaction(h1); + addr_phase.unlock(); + + data_phase.lock(); + scv_tr_handle h2 = wdata_gen.begin_transaction(req->data, "data_phase", h); + bus_data = req->data; + wait(data_rdy->posedge_event()); + wait(data_rdy->negedge_event()); + wdata_gen.end_transaction(h2); + write_gen.end_transaction(h, req->data); + data_phase.unlock(); +} + +class test : public sc_module { +public: + sc_port transactor; + SC_HAS_PROCESS(test); + test(::sc_core::sc_module_name) { + SC_THREAD(main1); + SC_THREAD(main2); + } + void main1(); + void main2(); +}; + +class write_constraint : virtual public scv_constraint_base { +public: + scv_smart_ptr write; + SCV_CONSTRAINT_CTOR(write_constraint) { // NOLINT + SCV_CONSTRAINT(write->addr() <= ram_size); // NOLINT + SCV_CONSTRAINT(write->addr() != write->data()); // NOLINT + } +}; + +inline void process(scv_smart_ptr data) {} + +inline void test::main1() { + // simple sequential tests + for (int i = 0; i < 3; i++) { + rw_task_if::addr_t addr = i; + rw_task_if::data_t data = transactor->read(&addr); + SCCINFO(sc_get_current_object()->name()) << "received data : " << data; + } + + scv_smart_ptr addr; + for (int i = 0; i < 3; i++) { + + addr->next(); + rw_task_if::data_t data = transactor->read(addr->get_instance()); + SCCINFO(sc_get_current_object()->name()) << "data for address " << *addr << " is " << data; + } + + scv_smart_ptr write; + for (int i = 0; i < 3; i++) { + write->next(); + transactor->write(write->get_instance()); + SCCINFO(sc_get_current_object()->name()) << "send data : " << write->data; + } + + scv_smart_ptr data; + scv_bag distribution; + distribution.push(1, 40); + distribution.push(2, 60); + data->set_mode(distribution); + for (int i = 0; i < 3; i++) { + data->next(); + process(data); + } +} + +inline void test::main2() { + // simple sequential tests + for (int i = 0; i < 3; i++) { + rw_task_if::addr_t addr = i; + rw_task_if::data_t data = transactor->read(&addr); + SCCINFO(sc_get_current_object()->name()) << "received data : " << data; + } + + scv_smart_ptr addr; + for (int i = 0; i < 3; i++) { + + addr->next(); + rw_task_if::data_t data = transactor->read(addr->get_instance()); + SCCINFO(sc_get_current_object()->name()) << "data for address " << *addr << " is " << data; + } + + scv_smart_ptr write; + for (int i = 0; i < 3; i++) { + write->next(); + transactor->write(write->get_instance()); + SCCINFO(sc_get_current_object()->name()) << "send data : " << write->data; + } + + scv_smart_ptr data; + scv_bag distribution; + distribution.push(1, 140); + distribution.push(2, 160); + data->set_mode(distribution); + for (int i = 0; i < 3; i++) { + data->next(); + process(data); + } +} +class design : public pipelined_bus_ports { + std::list> outstandingAddresses; + std::list outstandingType; + sc_uint<8> memory[ram_size]; + +public: + SC_HAS_PROCESS(design); + design(sc_module_name nm) + : pipelined_bus_ports(nm) { + for (unsigned i = 0; i < ram_size; ++i) { + memory[i] = i; + } + SC_THREAD(addr_phase); + SC_THREAD(data_phase); + } + void addr_phase(); + void data_phase(); +}; + +inline void design::addr_phase() { + while (true) { + while (addr_req.read() != 1) { + wait(addr_req->value_changed_event()); + } + sc_uint<8> _addr = bus_addr.read(); + bool _rw = rw.read(); + + int cycle = rand() % 10 + 1; + while (cycle-- > 0) { + wait(clk->posedge_event()); + } + + addr_ack = true; + wait(clk->posedge_event()); + addr_ack = false; + + outstandingAddresses.push_back(_addr); + outstandingType.push_back(_rw); + SCCINFO(sc_get_current_object()->name()) << "received request for memory address " << _addr; + } +} + +inline void design::data_phase() { + while (true) { + while (outstandingAddresses.empty()) { + wait(clk->posedge_event()); + } + int cycle = rand() % 10 + 1; + while (cycle-- > 0) { + wait(clk->posedge_event()); + } + if (outstandingType.front() == false) { + SCCINFO(sc_get_current_object()->name()) << "reading memory address " << outstandingAddresses.front() << " with value " + << memory[outstandingAddresses.front().to_ulong()]; + bus_data = memory[outstandingAddresses.front().to_ulong()]; + data_rdy = true; + wait(clk->posedge_event()); + data_rdy = false; + + } else { + SCCINFO(sc_get_current_object()->name()) << "writing memory address " << outstandingAddresses.front() << " with value " << bus_data; + memory[outstandingAddresses.front().to_ulong()] = bus_data; + data_rdy = true; + wait(clk->posedge_event()); + data_rdy = false; + } + outstandingAddresses.pop_front(); + outstandingType.pop_front(); + } +} + +inline const char* init_db(char type){ + switch(type){ + case '2': + scv_tr_compressed_init(); + return "my_db.txlog"; + break; + case '3': + scv_tr_sqlite_init(); + return "my_db.txdb"; + break; + default: + scv_tr_text_init(); + return "my_db.txlog"; + break; + } +} + +int sc_main(int argc, char *argv[]) { + auto start = std::chrono::system_clock::now(); + scv_startup(); + scc::init_logging(scc::LogConfig().logLevel(scc::log::DEBUG)); + const char *fileName = argc==2? init_db(argv[1][0]): "my_db.txlog"; + if(argc<2) scv_tr_text_init(); + scv_tr_db db(fileName); + scv_tr_db::set_default_db(&db); + sc_trace_file *tf = sc_create_vcd_trace_file("my_db"); + // create signals + sc_clock clk("clk", 20.0, SC_NS, 0.5, 0.0, SC_NS, true); + sc_signal rw; + sc_signal addr_req; + sc_signal addr_ack; + sc_signal> bus_addr; + sc_signal data_rdy; + sc_signal> bus_data; + + scc::value_registry registry; + // create modules/channels + test t("t"); + rw_pipelined_transactor tr("tr"); + design duv("duv"); + + // connect them up + t.transactor(tr); + + tr.clk(clk); + tr.rw(rw); + tr.addr_req(addr_req); + tr.addr_ack(addr_ack); + tr.bus_addr(bus_addr); + tr.data_rdy(data_rdy); + tr.bus_data(bus_data); + tr.trace(tf); + + duv.clk(clk); + duv.rw(rw); + duv.addr_req(addr_req); + duv.addr_ack(addr_ack); + duv.bus_addr(bus_addr); + duv.data_rdy(data_rdy); + duv.bus_data(bus_data); + duv.trace(tf); + + // Accellera SystemC >=2.2 got picky about multiple drivers. + // Disable check for bus simulation. + sc_report_handler::set_actions(SC_ID_MORE_THAN_ONE_SIGNAL_DRIVER_, SC_DO_NOTHING); + // run the simulation + sc_start(10.0, SC_US); + sc_close_vcd_trace_file(tf); + auto int_us = std::chrono::duration_cast(std::chrono::system_clock::now()-start); + SCCINFO() << "simulation duration "<::write_wdata(tlm::tlm_generic_payload& sc_dt::sc_uint strb{0}; auto ext = trans.get_extension(); auto size = 1u << ext->get_size(); - auto offset = trans.get_address() & (CFG::BUSWIDTH / 8 - 1); - auto beptr = trans.get_byte_enable_length() ? trans.get_byte_enable_ptr() + beat * size : nullptr; + auto byte_offset = beat * size; + auto offset = (trans.get_address()+byte_offset) & (CFG::BUSWIDTH / 8 - 1); + auto beptr = trans.get_byte_enable_length() ? trans.get_byte_enable_ptr() + byte_offset : nullptr; if(offset && (size + offset) > (CFG::BUSWIDTH / 8)) { // un-aligned multi-beat access if(beat == 0) { auto bptr = trans.get_data_ptr(); @@ -173,7 +174,7 @@ inline void axi::pin::axi4_initiator::write_wdata(tlm::tlm_generic_payload& strb[i] = true; } } else { - auto beat_start_idx = beat * size - offset; + auto beat_start_idx = byte_offset - offset; auto data_len = trans.get_data_length(); auto bptr = trans.get_data_ptr() + beat_start_idx; for(size_t i = offset; i < size && (beat_start_idx + i) < data_len; ++i, ++bptr) { @@ -187,15 +188,15 @@ inline void axi::pin::axi4_initiator::write_wdata(tlm::tlm_generic_payload& } } } else { // aligned or single beat access - auto bptr = trans.get_data_ptr() + beat * size; + auto bptr = trans.get_data_ptr() + byte_offset; for(size_t i = 0; i < size; ++i, ++bptr) { - auto bit_offs = (offset + i) * 8; + auto bit_offs = (offset+i) * 8; data(bit_offs + 7, bit_offs) = *bptr; if(beptr) { - strb[i] = *beptr == 0xff; + strb[offset+i] = *beptr == 0xff; ++beptr; } else - strb[i] = true; + strb[offset+i] = true; } } this->w_data.write(data); @@ -303,8 +304,9 @@ template inline void axi::pin::axi4_initiator::setup_callbac } template inline void axi::pin::axi4_initiator::ar_t() { + this->ar_valid.write(false); + wait(sc_core::SC_ZERO_TIME); while(true) { - this->ar_valid.write(false); wait(ar_evt); this->ar_valid.write(true); do { @@ -313,14 +315,17 @@ template inline void axi::pin::axi4_initiator::ar_t() { react(axi::fsm::protocol_time_point_e::EndReqE, active_req[tlm::TLM_READ_COMMAND]); } while(!this->ar_ready.read()); wait(clk_i.posedge_event()); + this->ar_valid.write(false); } } template inline void axi::pin::axi4_initiator::r_t() { this->r_ready.write(false); + wait(sc_core::SC_ZERO_TIME); while(true) { wait(this->r_valid.posedge_event() | clk_i.negedge_event()); if(this->r_valid.event() || (!active_resp[tlm::TLM_READ_COMMAND] && this->r_valid.read())) { + wait(sc_core::SC_ZERO_TIME); auto id = CFG::IS_LITE ? 0U : this->r_id->read().to_uint(); auto data = this->r_data.read(); auto resp = this->r_resp.read(); @@ -329,7 +334,8 @@ template inline void axi::pin::axi4_initiator::r_t() { auto* fsm_hndl = q.front(); auto beat_count = fsm_hndl->beat_count; auto size = axi::get_burst_size(*fsm_hndl->trans); - auto offset = fsm_hndl->trans->get_address() & (CFG::BUSWIDTH / 8 - 1); + auto byte_offset = beat_count * size; + auto offset = (fsm_hndl->trans->get_address()+byte_offset) & (CFG::BUSWIDTH / 8 - 1); if(offset && (size + offset) > (CFG::BUSWIDTH / 8)) { // un-aligned multi-beat access if(beat_count == 0) { auto bptr = fsm_hndl->trans->get_data_ptr(); @@ -349,7 +355,7 @@ template inline void axi::pin::axi4_initiator::r_t() { } else { // aligned or single beat access auto bptr = fsm_hndl->trans->get_data_ptr() + beat_count * size; for(size_t i = 0; i < size; ++i, ++bptr) { - auto bit_offs = (offset + i) * 8; + auto bit_offs = (offset+i) * 8; *bptr = data(bit_offs + 7, bit_offs).to_uint(); } } @@ -369,20 +375,23 @@ template inline void axi::pin::axi4_initiator::r_t() { } template inline void axi::pin::axi4_initiator::aw_t() { + this->aw_valid.write(false); + wait(sc_core::SC_ZERO_TIME); while(true) { - this->aw_valid.write(false); wait(aw_evt); this->aw_valid.write(true); do { wait(this->aw_ready.posedge_event() | clk_delayed); } while(!this->aw_ready.read()); wait(clk_i.posedge_event()); + this->aw_valid.write(false); } } template inline void axi::pin::axi4_initiator::wdata_t() { + this->w_valid.write(false); + wait(sc_core::SC_ZERO_TIME); while(true) { - this->w_valid.write(false); if(!CFG::IS_LITE) this->w_last->write(false); wait(wdata_vl.default_event()); @@ -399,11 +408,13 @@ template inline void axi::pin::axi4_initiator::wdata_t() { } } while(!this->w_ready.read()); wait(clk_i.posedge_event()); + this->w_valid.write(false); } } template inline void axi::pin::axi4_initiator::b_t() { this->b_ready.write(false); + wait(sc_core::SC_ZERO_TIME); while(true) { wait(this->b_valid.posedge_event() | clk_i.negedge_event()); if(this->b_valid.event() || (!active_resp[tlm::TLM_WRITE_COMMAND] && this->b_valid.read())) { diff --git a/src/bus_interfaces/axi/pin/axi4_target.h b/src/bus_interfaces/axi/pin/axi4_target.h index 376b3a8a..425c940a 100644 --- a/src/bus_interfaces/axi/pin/axi4_target.h +++ b/src/bus_interfaces/axi/pin/axi4_target.h @@ -128,7 +128,8 @@ inline void axi::pin::axi4_target::invalidate_direct_mem_ptr(sc_dt::uint64 template typename CFG::data_t axi::pin::axi4_target::get_read_data_for_beat(fsm_handle* fsm_hndl) { auto beat_count = fsm_hndl->beat_count; auto size = axi::get_burst_size(*fsm_hndl->trans); - auto offset = fsm_hndl->trans->get_address() & (CFG::BUSWIDTH / 8 - 1); + auto byte_offset = beat_count * size; + auto offset = (fsm_hndl->trans->get_address()+byte_offset) & (CFG::BUSWIDTH / 8 - 1); typename CFG::data_t data{0}; if(offset && (size + offset) > (CFG::BUSWIDTH / 8)) { // un-aligned multi-beat access if(beat_count == 0) { @@ -138,7 +139,7 @@ template typename CFG::data_t axi::pin::axi4_target::get_rea data(bit_offs + 7, bit_offs) = *bptr; } } else { - auto beat_start_idx = beat_count * size - offset; + auto beat_start_idx = byte_offset - offset; auto data_len = fsm_hndl->trans->get_data_length(); auto bptr = fsm_hndl->trans->get_data_ptr() + beat_start_idx; for(size_t i = offset; i < size && (beat_start_idx + i) < data_len; ++i, ++bptr) { @@ -147,7 +148,7 @@ template typename CFG::data_t axi::pin::axi4_target::get_rea } } } else { // aligned or single beat access - auto bptr = fsm_hndl->trans->get_data_ptr() + beat_count * size; + auto bptr = fsm_hndl->trans->get_data_ptr() + byte_offset; for(size_t i = 0; i < size; ++i, ++bptr) { auto bit_offs = (offset + i) * 8; data(bit_offs + 7, bit_offs) = *bptr; @@ -235,6 +236,7 @@ template inline void axi::pin::axi4_target::setup_callbacks( template inline void axi::pin::axi4_target::ar_t() { this->ar_ready.write(false); + wait(sc_core::SC_ZERO_TIME); auto arid = 0U; auto arlen = 0U; auto arsize = util::ilog2(CFG::BUSWIDTH / 8); @@ -270,6 +272,8 @@ template inline void axi::pin::axi4_target::ar_t() { } template inline void axi::pin::axi4_target::rresp_t() { + this->r_valid.write(false); + wait(sc_core::SC_ZERO_TIME); fsm_handle* fsm_hndl; uint8_t val; while(true) { @@ -301,6 +305,7 @@ template inline void axi::pin::axi4_target::rresp_t() { template inline void axi::pin::axi4_target::aw_t() { this->aw_ready.write(false); + wait(sc_core::SC_ZERO_TIME); const auto awsize = util::ilog2(CFG::BUSWIDTH / 8); while(true) { wait(this->aw_valid.posedge_event() | clk_i.negedge_event()); @@ -328,6 +333,7 @@ template inline void axi::pin::axi4_target::aw_t() { template inline void axi::pin::axi4_target::wdata_t() { this->w_ready.write(false); + wait(sc_core::SC_ZERO_TIME); while(true) { wait(this->w_valid.posedge_event() | clk_i.negedge_event()); this->w_ready.write(false); @@ -365,7 +371,8 @@ template inline void axi::pin::axi4_target::wdata_t() { auto last = CFG::IS_LITE ? true : this->w_last->read(); auto beat_count = fsm_hndl->beat_count; auto size = axi::get_burst_size(*fsm_hndl->trans); - auto offset = fsm_hndl->trans->get_address() & (CFG::BUSWIDTH / 8 - 1); + auto byte_offset = beat_count * size; + auto offset = (fsm_hndl->trans->get_address()+byte_offset) & (CFG::BUSWIDTH / 8 - 1); if(offset && (size + offset) > (CFG::BUSWIDTH / 8)) { // un-aligned multi-beat access if(beat_count == 0) { auto bptr = fsm_hndl->trans->get_data_ptr(); @@ -376,7 +383,7 @@ template inline void axi::pin::axi4_target::wdata_t() { *beptr = strb[i] ? 0xff : 0; } } else { - auto beat_start_idx = beat_count * size - offset; + auto beat_start_idx = byte_offset - offset; auto data_len = fsm_hndl->trans->get_data_length(); auto bptr = fsm_hndl->trans->get_data_ptr() + beat_start_idx; auto beptr = fsm_hndl->trans->get_byte_enable_ptr() + beat_start_idx; @@ -387,12 +394,12 @@ template inline void axi::pin::axi4_target::wdata_t() { } } } else { // aligned or single beat access - auto bptr = fsm_hndl->trans->get_data_ptr() + beat_count * size; - auto beptr = fsm_hndl->trans->get_byte_enable_ptr() + beat_count * size; + auto bptr = fsm_hndl->trans->get_data_ptr() + byte_offset; + auto beptr = fsm_hndl->trans->get_byte_enable_ptr() + byte_offset; for(size_t i = 0; i < size; ++i, ++bptr, ++beptr) { - auto bit_offs = (offset + i) * 8; + auto bit_offs = (offset+i) * 8; *bptr = data(bit_offs + 7, bit_offs).to_uint(); - *beptr = strb[i] ? 0xff : 0; + *beptr = strb[offset +i] ? 0xff : 0; } } // TODO: assuming consecutive write (not scattered) @@ -415,6 +422,8 @@ template inline void axi::pin::axi4_target::wdata_t() { template inline void axi::pin::axi4_target::bresp_t() { + this->b_valid.write(false); + wait(sc_core::SC_ZERO_TIME); fsm_handle* fsm_hndl; uint8_t val; while(true){ diff --git a/src/components/scc/tlm_target.h b/src/components/scc/tlm_target.h index a8fb34b9..001d3e32 100644 --- a/src/components/scc/tlm_target.h +++ b/src/components/scc/tlm_target.h @@ -22,6 +22,7 @@ #include "tlm/scc/target_mixin.h" #include "util/range_lut.h" #include +#include #include namespace scc { @@ -128,6 +129,10 @@ inline scc::tlm_target::tlm_target(sc_core::sc_time& socket.register_transport_dbg([=](tlm::tlm_generic_payload& gp) -> unsigned { return this->tranport_dbg_cb(gp); }); } +inline bool valid_byte_enable(tlm::tlm_generic_payload const& gp){ + return gp.get_byte_enable_length()==gp.get_data_length() && + std::accumulate(gp.get_byte_enable_ptr(), gp.get_byte_enable_ptr()+gp.get_byte_enable_length(), 1, std::multiplies())!=0; +} template void scc::tlm_target::b_tranport_cb(tlm::tlm_generic_payload& gp, sc_core::sc_time& delay) { resource_access_if* ra = nullptr; @@ -137,7 +142,7 @@ void scc::tlm_target::b_tranport_cb(tlm::tlm_generic_ gp.set_response_status(tlm::TLM_BURST_ERROR_RESPONSE); if(gp.get_data_length() <= ra->size()) { gp.set_response_status(tlm::TLM_BYTE_ENABLE_ERROR_RESPONSE); - if(gp.get_byte_enable_ptr() == nullptr) { + if(gp.get_byte_enable_ptr() == nullptr || valid_byte_enable(gp)) { gp.set_response_status(tlm::TLM_GENERIC_ERROR_RESPONSE); if(gp.get_data_length() == gp.get_streaming_width()) { if(gp.get_command() == tlm::TLM_READ_COMMAND) { diff --git a/src/sysc/scc/configurable_tracer.cpp b/src/sysc/scc/configurable_tracer.cpp index 9f22c1ab..52b4c9c9 100644 --- a/src/sysc/scc/configurable_tracer.cpp +++ b/src/sysc/scc/configurable_tracer.cpp @@ -97,8 +97,8 @@ auto scc::configurable_tracer::get_trace_enabled(const sc_core::sc_object* obj, return fall_back; } -void configurable_tracer::augment_object_hierarchical(const sc_core::sc_object* obj) { - if(dynamic_cast(obj) != nullptr || dynamic_cast(obj) != nullptr) { +void configurable_tracer::augment_object_hierarchical(sc_core::sc_object* obj) { + if(dynamic_cast(obj) != nullptr || dynamic_cast(obj) != nullptr) { auto* attr = obj->get_attribute(EN_TRACING_STR); if(attr == nullptr || dynamic_cast*>(attr) == nullptr) { // check if we have no sc_attribute @@ -108,6 +108,10 @@ void configurable_tracer::augment_object_hierarchical(const sc_core::sc_object* if(!h.is_valid()) // we have no cci_param so create one params.push_back(new cci::cci_param(hier_name, default_trace_enable, "", cci::CCI_ABSOLUTE_NAME, cci_originator)); + else + h.set_cci_value(cci::cci_value{default_trace_enable}); + } else if(auto battr = dynamic_cast*>(attr)) { + battr->value = default_trace_enable; } for(auto* o : obj->get_child_objects()) augment_object_hierarchical(o); diff --git a/src/sysc/scc/configurable_tracer.h b/src/sysc/scc/configurable_tracer.h index 786fa51d..0ed2e2f0 100644 --- a/src/sysc/scc/configurable_tracer.h +++ b/src/sysc/scc/configurable_tracer.h @@ -98,7 +98,7 @@ class configurable_tracer : public tracer { //! check for existence of 'enableTracing' attribute and return value of default otherwise bool get_trace_enabled(const sc_core::sc_object*, bool = false); //! add the 'enableTracing' attribute to sc_module - void augment_object_hierarchical(const sc_core::sc_object*); + void augment_object_hierarchical(sc_core::sc_object*); void end_of_elaboration() override; //! the originator of cci values diff --git a/src/sysc/scc/ordered_semaphore.cpp b/src/sysc/scc/ordered_semaphore.cpp index 4c659886..ad1915b1 100644 --- a/src/sysc/scc/ordered_semaphore.cpp +++ b/src/sysc/scc/ordered_semaphore.cpp @@ -109,9 +109,9 @@ auto ordered_semaphore::trywait() -> int { // unlock (give) the semaphore auto ordered_semaphore::post() -> int { - if(capacity && value == capacity) + if(capacity && value == capacity) { SCCWARN(SCMOD) << "post() called on entirely free semaphore!"; - else + } else ++value; if(value > 0) free_evt.notify(); diff --git a/src/sysc/scc/report.cpp b/src/sysc/scc/report.cpp index d24c4c4a..04a88ff1 100644 --- a/src/sysc/scc/report.cpp +++ b/src/sysc/scc/report.cpp @@ -529,20 +529,36 @@ auto scc::get_log_verbosity(char const* str) -> sc_core::sc_verbosity { if(it != lut.end()) return it->second; if(strchr(str, '.') == nullptr || sc_core::sc_get_current_object()) { + string current_name = std::string(str); auto broker = sc_core::sc_get_current_object()? cci::cci_get_broker(): cci::cci_get_global_broker(originator); - auto param_name = std::string(str) + "." SCC_LOG_LEVEL_PARAM_NAME; - auto h = broker.get_param_handle(param_name); - if(h.is_valid()) { - sc_core::sc_verbosity ret = verbosity.at(std::min(h.get_value(), verbosity.size() - 1)); - lut[k] = ret; - return ret; - } else { - auto val = broker.get_preset_cci_value(param_name); - auto global_verb = static_cast(::sc_core::sc_report_handler::get_verbosity_level()); - sc_core::sc_verbosity ret = - val.is_int() ? verbosity.at(std::min(val.get_int(), verbosity.size() - 1)) : global_verb; - lut[k] = ret; - return ret; + while (true) { + string param_name = (current_name.empty())? SCC_LOG_LEVEL_PARAM_NAME : current_name + "." SCC_LOG_LEVEL_PARAM_NAME; + auto h = broker.get_param_handle(param_name); + if (h.is_valid()) { + sc_core::sc_verbosity ret = verbosity.at(std::min(h.get_cci_value().get_int(), verbosity.size() - 1)); + lut[k] = ret; + return ret; + } else { + auto val = broker.get_preset_cci_value(param_name); + auto global_verb = static_cast(::sc_core::sc_report_handler::get_verbosity_level()); + if (val.is_int()) { + sc_core::sc_verbosity ret = verbosity.at(std::min(val.get_int(), verbosity.size() - 1)); + lut[k] = ret; + return ret; + } else { + if (current_name.empty()) { + sc_core::sc_verbosity ret = global_verb; + lut[k] = ret; + return ret; + } + auto pos = current_name.rfind("."); + if (pos == std::string::npos) { + current_name = ""; + } else { + current_name = current_name.substr(0, pos); + } + } + } } } } diff --git a/src/sysc/scc/report.h b/src/sysc/scc/report.h index 057be073..c3e75edd 100644 --- a/src/sysc/scc/report.h +++ b/src/sysc/scc/report.h @@ -360,7 +360,7 @@ template struct ScLogger { //! macro for info level output #define SCCINFO(...) if(::scc::get_log_verbosity(__VA_ARGS__) >= sc_core::SC_MEDIUM) SCCLOG(sc_core::SC_MEDIUM, __VA_ARGS__) //! macro for warning level output -#define SCCWARN(...) ::scc::ScLogger<::sc_core::SC_WARNING>(__FILE__, __LINE__, sc_core::SC_MEDIUM).type(__VA_ARGS__).get() +#define SCCWARN(...) if(::scc::get_log_verbosity(__VA_ARGS__) >= sc_core::SC_LOW) ::scc::ScLogger<::sc_core::SC_WARNING>(__FILE__, __LINE__, sc_core::SC_MEDIUM).type(__VA_ARGS__).get() //! macro for error level output #define SCCERR(...) ::scc::ScLogger<::sc_core::SC_ERROR>(__FILE__, __LINE__, sc_core::SC_MEDIUM).type(__VA_ARGS__).get() //! macro for fatal message output diff --git a/third_party/axi_chi b/third_party/axi_chi index a98a38b4..0974c43d 160000 --- a/third_party/axi_chi +++ b/third_party/axi_chi @@ -1 +1 @@ -Subproject commit a98a38b490acb144ec15e989f41bf7f7a5bd561e +Subproject commit 0974c43df24bd0a76d275188019bbbc1d0bfbd41