diff --git a/examples/simple_system/gen/e300_plat_t.h b/examples/simple_system/gen/e300_plat_t.h index cfb8984..fe8279d 100644 --- a/examples/simple_system/gen/e300_plat_t.h +++ b/examples/simple_system/gen/e300_plat_t.h @@ -1,11 +1,26 @@ +/******************************************************************************* + * 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 = {{ - {&i_plic, 0xc000000, 0x1000}, + {&i_plic, 0x0c000000, 0x200008}, {&i_gpio, 0x10012000, 0x1000}, {&i_uart, 0x10013000, 0x1000}, - {&i_spi, 0x10014000, 0x1000}, + {&i_spi, 0x10014000, 0x1000}, }}; #endif /* _E300_PLAT_MAP_H_ */ diff --git a/examples/simple_system/gen/plic_regs.h b/examples/simple_system/gen/plic_regs.h index 936873c..8ed3126 100644 --- a/examples/simple_system/gen/plic_regs.h +++ b/examples/simple_system/gen/plic_regs.h @@ -47,7 +47,7 @@ class plic_regs : public sc_core::sc_module, public sysc::resetable { -protected: +public: // storage declarations BEGIN_BF_DECL(priority_t, uint32_t); BF_FIELD(priority, 0, 3); @@ -71,7 +71,6 @@ protected: sysc::sc_register threshold; sysc::sc_register claim_complete; -public: plic_regs(sc_core::sc_module_name nm); template @@ -97,8 +96,8 @@ inline void sysc::plic_regs::registerResources(sysc::tlm_target& targe target.addResource(priority, 0x4UL); target.addResource(pending, 0x1000UL); target.addResource(enabled, 0x2000UL); - target.addResource(threshold, 0xc200000UL); - target.addResource(claim_complete, 0xc200004UL); + target.addResource(threshold, 0x00200000UL); + target.addResource(claim_complete, 0x00200004UL); } #endif // _PLIC_REGS_H_ diff --git a/examples/simple_system/plic.cpp b/examples/simple_system/plic.cpp index 69f4304..ffca22d 100644 --- a/examples/simple_system/plic.cpp +++ b/examples/simple_system/plic.cpp @@ -1,22 +1,45 @@ //////////////////////////////////////////////////////////////////////////////// -// 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. +// 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 "sysc/utilities.h" +#include namespace sysc { @@ -25,27 +48,146 @@ plic::plic(sc_core::sc_module_name nm) , tlm_target<>(clk) , NAMED(clk_i) , NAMED(rst_i) +, NAMED(global_interrupts_i, 256) +, NAMED(core_interrupt_o) , NAMEDD(plic_regs, 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< 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() { +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); + LOG(logging::INFO) << "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 = 0; + 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 = 1; + LOG(logging::INFO) << "pending interrupt activated: " << i; + } + } + } + + if ( raise_int ) { + regs->r_claim_complete = claim_int; + core_interrupt_o.write(1); + // todo: evluate clock period + } else { + regs->r_claim_complete = 0; + LOG(logging::INFO) << "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. + LOG(logging::INFO) << "reset pending interrupt: " << irq; + // reset related pending bit + regs->r_pending &= ~(0x1 << irq); + core_interrupt_o.write(0); + + // evaluate next pending interrupt + handle_pending_int(); +} + + } /* namespace sysc */ diff --git a/examples/simple_system/plic.h b/examples/simple_system/plic.h index 3ad1e5b..e05fab7 100644 --- a/examples/simple_system/plic.h +++ b/examples/simple_system/plic.h @@ -18,6 +18,7 @@ #define _PLIC_H_ #include +#include namespace sysc { @@ -26,15 +27,28 @@ class plic_regs; class plic: public sc_core::sc_module, public tlm_target<> { public: SC_HAS_PROCESS(plic); - sc_core::sc_in clk_i; - sc_core::sc_in rst_i; + 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 */ diff --git a/examples/simple_system/sc_main.cpp b/examples/simple_system/sc_main.cpp index 5055652..7c9745e 100644 --- a/examples/simple_system/sc_main.cpp +++ b/examples/simple_system/sc_main.cpp @@ -89,7 +89,7 @@ int sc_main(int argc, char* argv[]){ if(!sc_core::sc_end_of_simulation_invoked()) { LOG(logging::ERROR) << "simulation timed out"; - sc_stop(); + sc_core::sc_stop(); } return 0; } diff --git a/examples/simple_system/simple_system.cpp b/examples/simple_system/simple_system.cpp index 4295ae4..8a21b73 100644 --- a/examples/simple_system/simple_system.cpp +++ b/examples/simple_system/simple_system.cpp @@ -34,14 +34,21 @@ simple_system::simple_system(sc_core::sc_module_name nm) , NAMED(i_plic) , NAMED(s_clk) , NAMED(s_rst) +, NAMED(s_global_interrupts, 256) +, NAMED(s_core_interrupt) { + // todo: discuss naming conventions (s_ vs. _i/_o) --> covnert into _s + + // bus connections i_master.intor(i_router.target[0]); size_t i=0; for(const auto& e: e300_plat_map){ - i_router.initiator.at(i)(e.target->socket); + i_router.initiator[i](e.target->socket); i_router.add_target_range(i, e.start, e.size); i++; } + + // clock/reset connections i_uart.clk_i(s_clk); i_spi.clk_i(s_clk); i_gpio.clk_i(s_clk); @@ -54,6 +61,12 @@ simple_system::simple_system(sc_core::sc_module_name nm) 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); + SC_THREAD(gen_reset); } diff --git a/examples/simple_system/simple_system.h b/examples/simple_system/simple_system.h index 29c9085..46066a1 100644 --- a/examples/simple_system/simple_system.h +++ b/examples/simple_system/simple_system.h @@ -48,6 +48,8 @@ public: 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; simple_system(sc_core::sc_module_name nm); protected: diff --git a/examples/simple_system/test_initiator.cpp b/examples/simple_system/test_initiator.cpp index f5bd85f..62418af 100644 --- a/examples/simple_system/test_initiator.cpp +++ b/examples/simple_system/test_initiator.cpp @@ -21,43 +21,266 @@ */ #include "test_initiator.h" -#include +#include #include #include -namespace sysc { +// 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 { test_initiator::test_initiator(sc_core::sc_module_name nm) : sc_core::sc_module(nm) , NAMED(intor) , NAMED(rst_i) +, NAMED(global_interrupts_o, 256) +, NAMED(core_interrupt_i) { - SC_THREAD(run); + SC_THREAD(run); + SC_METHOD(core_irq_handler); + sensitive << core_interrupt_i; + dont_initialize(); } -void test_initiator::run() { +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 ??? +} + +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(1); + wait(10_ns); + global_interrupts_o[2].write(0); + 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 + + uint32_t v = 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(1); + wait(10_ns); + global_interrupts_o[1].write(0); + 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(1); + wait(10_ns); + global_interrupts_o[1].write(0); + wait(10_ns); + global_interrupts_o[2].write(1); + wait(10_ns); + global_interrupts_o[2].write(0); + wait(10_ns); + global_interrupts_o[3].write(1); + wait(10_ns); + global_interrupts_o[3].write(0); + 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; - srInfo()("group", "comm")("read access"); - gp.set_command(tlm::TLM_READ_COMMAND); -// gp.set_address(0x10012000); - gp.set_address(0xc000004); + data[3] = 0xff & dat>>24; + data[2] = 0xff & dat>>16; + data[1] = 0xff & dat>>8; + data[0] = 0xff & dat; + + LOG(logging::INFO) << "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_core::sc_time delay; intor->b_transport(gp, delay); - wait(10_ns); - srWarn()("group", "comm")("write access"); - gp.set_command(tlm::TLM_WRITE_COMMAND); - gp.set_address(0x10012000); - data[0]=0xA5; + + 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_core::sc_time delay; intor->b_transport(gp, delay); - wait(10_ns); + + 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]; + + LOG(logging::INFO) << "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 ) { + LOG(logging::ERROR) << "register check failed for address 0x" << std::hex << adr << ": " << dat << " != " << exp; + } else { + LOG(logging::INFO) << "register check passed for address 0x" << std::hex << adr << ": " << dat; + } +} + +void test_initiator::core_irq_handler() +{ + LOG(logging::INFO) << "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 index f3e166a..134677b 100644 --- a/examples/simple_system/test_initiator.h +++ b/examples/simple_system/test_initiator.h @@ -32,11 +32,21 @@ class test_initiator: public sc_core::sc_module { public: SC_HAS_PROCESS(test_initiator); tlm_utils::simple_initiator_socket intor; - - sc_core::sc_in rst_i; + 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 */