first draft version of PLIC
This commit is contained in:
		| @@ -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<sysc::target_memory_map_entry<32>, 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_ */ | ||||
|   | ||||
| @@ -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_t> threshold; | ||||
|     sysc::sc_register<uint32_t> claim_complete; | ||||
|      | ||||
| public: | ||||
|     plic_regs(sc_core::sc_module_name nm); | ||||
|  | ||||
|     template<unsigned BUSWIDTH=32> | ||||
| @@ -97,8 +96,8 @@ inline void sysc::plic_regs::registerResources(sysc::tlm_target<BUSWIDTH>& 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_ | ||||
|   | ||||
| @@ -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 <sysc/report.h> | ||||
|  | ||||
| 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<<clk_i; | ||||
|     SC_METHOD(reset_cb); | ||||
|     sensitive<<rst_i; | ||||
|  | ||||
| } | ||||
|  | ||||
| plic::~plic() { | ||||
| } | ||||
|  | ||||
| void plic::clock_cb() { | ||||
| void plic::init_callbacks() | ||||
| { | ||||
| 	m_claim_complete_write_cb = [=](sysc::sc_register<uint32_t> 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 */ | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #define _PLIC_H_ | ||||
|  | ||||
| #include <sysc/tlm_target.h> | ||||
| #include <sysc/register.h> | ||||
|  | ||||
| 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<sc_core::sc_time> clk_i; | ||||
|     sc_core::sc_in<bool>             rst_i; | ||||
|     sc_core::sc_in<sc_core::sc_time>          clk_i; | ||||
|     sc_core::sc_in<bool>                      rst_i; | ||||
|     sc_core::sc_vector<sc_core::sc_in<bool>>  global_interrupts_i; | ||||
|     sc_core::sc_out<bool>                     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<plic_regs> regs; | ||||
|     std::function<bool(sc_register<uint32_t>,uint32_t)> m_claim_complete_write_cb; | ||||
| }; | ||||
|  | ||||
| } /* namespace sysc */ | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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_<signal> vs. <port>_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); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -48,6 +48,8 @@ public: | ||||
|     plic i_plic; | ||||
|     sc_core::sc_signal<sc_core::sc_time> s_clk; | ||||
|     sc_core::sc_signal<bool> s_rst; | ||||
|     sc_core::sc_vector<sc_core::sc_signal<bool>> s_global_interrupts; | ||||
|     sc_core::sc_signal<bool> s_core_interrupt; | ||||
|  | ||||
|     simple_system(sc_core::sc_module_name nm); | ||||
| protected: | ||||
|   | ||||
| @@ -21,43 +21,266 @@ | ||||
|  */ | ||||
|  | ||||
| #include "test_initiator.h" | ||||
| #include <sr_report/sr_report.h> | ||||
| #include <sysc/report.h> | ||||
| #include <sysc/utilities.h> | ||||
| #include <array> | ||||
|  | ||||
| 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<uint8_t, 4> 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<uint8_t, 4> 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 */ | ||||
|   | ||||
| @@ -32,11 +32,21 @@ class test_initiator: public sc_core::sc_module { | ||||
| public: | ||||
|     SC_HAS_PROCESS(test_initiator); | ||||
|     tlm_utils::simple_initiator_socket<test_initiator, 32> intor; | ||||
|  | ||||
|     sc_core::sc_in<bool> rst_i; | ||||
|     sc_core::sc_vector<sc_core::sc_out<bool>>              global_interrupts_o; | ||||
|     sc_core::sc_in<bool>                                   core_interrupt_i; | ||||
|     sc_core::sc_in<bool>                                   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 */ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Soeren Jung
					Soeren Jung