first draft version of PLIC
This commit is contained in:
parent
4b25eb0b29
commit
f2fc62fbb8
|
@ -1,8 +1,23 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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_
|
#ifndef _E300_PLAT_MAP_H_
|
||||||
#define _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
|
// 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 = {{
|
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_gpio, 0x10012000, 0x1000},
|
||||||
{&i_uart, 0x10013000, 0x1000},
|
{&i_uart, 0x10013000, 0x1000},
|
||||||
{&i_spi, 0x10014000, 0x1000},
|
{&i_spi, 0x10014000, 0x1000},
|
||||||
|
|
|
@ -47,7 +47,7 @@ class plic_regs :
|
||||||
public sc_core::sc_module,
|
public sc_core::sc_module,
|
||||||
public sysc::resetable
|
public sysc::resetable
|
||||||
{
|
{
|
||||||
protected:
|
public:
|
||||||
// storage declarations
|
// storage declarations
|
||||||
BEGIN_BF_DECL(priority_t, uint32_t);
|
BEGIN_BF_DECL(priority_t, uint32_t);
|
||||||
BF_FIELD(priority, 0, 3);
|
BF_FIELD(priority, 0, 3);
|
||||||
|
@ -71,7 +71,6 @@ protected:
|
||||||
sysc::sc_register<threshold_t> threshold;
|
sysc::sc_register<threshold_t> threshold;
|
||||||
sysc::sc_register<uint32_t> claim_complete;
|
sysc::sc_register<uint32_t> claim_complete;
|
||||||
|
|
||||||
public:
|
|
||||||
plic_regs(sc_core::sc_module_name nm);
|
plic_regs(sc_core::sc_module_name nm);
|
||||||
|
|
||||||
template<unsigned BUSWIDTH=32>
|
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(priority, 0x4UL);
|
||||||
target.addResource(pending, 0x1000UL);
|
target.addResource(pending, 0x1000UL);
|
||||||
target.addResource(enabled, 0x2000UL);
|
target.addResource(enabled, 0x2000UL);
|
||||||
target.addResource(threshold, 0xc200000UL);
|
target.addResource(threshold, 0x00200000UL);
|
||||||
target.addResource(claim_complete, 0xc200004UL);
|
target.addResource(claim_complete, 0x00200004UL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // _PLIC_REGS_H_
|
#endif // _PLIC_REGS_H_
|
||||||
|
|
|
@ -1,22 +1,45 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Copyright 2017 eyck@minres.com
|
// Copyright (C) 2017, MINRES Technologies GmbH
|
||||||
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
// Redistribution and use in source and binary forms, with or without
|
||||||
// use this file except in compliance with the License. You may obtain a copy
|
// modification, are permitted provided that the following conditions are met:
|
||||||
// of the License at
|
//
|
||||||
|
// 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
|
||||||
//
|
//
|
||||||
// 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.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// todo: truncate values beyond 7 (in prio_threshold write_cb)
|
||||||
|
|
||||||
#include "plic.h"
|
#include "plic.h"
|
||||||
#include "gen/plic_regs.h"
|
#include "gen/plic_regs.h"
|
||||||
#include "sysc/utilities.h"
|
#include "sysc/utilities.h"
|
||||||
|
#include <sysc/report.h>
|
||||||
|
|
||||||
namespace sysc {
|
namespace sysc {
|
||||||
|
|
||||||
|
@ -25,27 +48,146 @@ plic::plic(sc_core::sc_module_name nm)
|
||||||
, tlm_target<>(clk)
|
, tlm_target<>(clk)
|
||||||
, NAMED(clk_i)
|
, NAMED(clk_i)
|
||||||
, NAMED(rst_i)
|
, NAMED(rst_i)
|
||||||
|
, NAMED(global_interrupts_i, 256)
|
||||||
|
, NAMED(core_interrupt_o)
|
||||||
, NAMEDD(plic_regs, regs)
|
, NAMEDD(plic_regs, regs)
|
||||||
|
|
||||||
{
|
{
|
||||||
regs->registerResources(*this);
|
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);
|
SC_METHOD(clock_cb);
|
||||||
sensitive<<clk_i;
|
sensitive<<clk_i;
|
||||||
SC_METHOD(reset_cb);
|
SC_METHOD(reset_cb);
|
||||||
sensitive<<rst_i;
|
sensitive<<rst_i;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plic::~plic() {
|
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();
|
this->clk=clk_i.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
void plic::reset_cb() {
|
void plic::reset_cb()
|
||||||
|
{
|
||||||
if(rst_i.read())
|
if(rst_i.read())
|
||||||
regs->reset_start();
|
regs->reset_start();
|
||||||
else
|
else
|
||||||
regs->reset_stop();
|
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 */
|
} /* namespace sysc */
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define _PLIC_H_
|
#define _PLIC_H_
|
||||||
|
|
||||||
#include <sysc/tlm_target.h>
|
#include <sysc/tlm_target.h>
|
||||||
|
#include <sysc/register.h>
|
||||||
|
|
||||||
namespace sysc {
|
namespace sysc {
|
||||||
|
|
||||||
|
@ -28,13 +29,26 @@ public:
|
||||||
SC_HAS_PROCESS(plic);
|
SC_HAS_PROCESS(plic);
|
||||||
sc_core::sc_in<sc_core::sc_time> clk_i;
|
sc_core::sc_in<sc_core::sc_time> clk_i;
|
||||||
sc_core::sc_in<bool> rst_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);
|
plic(sc_core::sc_module_name nm);
|
||||||
virtual ~plic();
|
virtual ~plic();
|
||||||
protected:
|
protected:
|
||||||
void clock_cb();
|
void clock_cb();
|
||||||
void reset_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;
|
sc_core::sc_time clk;
|
||||||
std::unique_ptr<plic_regs> regs;
|
std::unique_ptr<plic_regs> regs;
|
||||||
|
std::function<bool(sc_register<uint32_t>,uint32_t)> m_claim_complete_write_cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* namespace sysc */
|
} /* namespace sysc */
|
||||||
|
|
|
@ -89,7 +89,7 @@ int sc_main(int argc, char* argv[]){
|
||||||
|
|
||||||
if(!sc_core::sc_end_of_simulation_invoked()) {
|
if(!sc_core::sc_end_of_simulation_invoked()) {
|
||||||
LOG(logging::ERROR) << "simulation timed out";
|
LOG(logging::ERROR) << "simulation timed out";
|
||||||
sc_stop();
|
sc_core::sc_stop();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,14 +34,21 @@ simple_system::simple_system(sc_core::sc_module_name nm)
|
||||||
, NAMED(i_plic)
|
, NAMED(i_plic)
|
||||||
, NAMED(s_clk)
|
, NAMED(s_clk)
|
||||||
, NAMED(s_rst)
|
, 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]);
|
i_master.intor(i_router.target[0]);
|
||||||
size_t i=0;
|
size_t i=0;
|
||||||
for(const auto& e: e300_plat_map){
|
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_router.add_target_range(i, e.start, e.size);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clock/reset connections
|
||||||
i_uart.clk_i(s_clk);
|
i_uart.clk_i(s_clk);
|
||||||
i_spi.clk_i(s_clk);
|
i_spi.clk_i(s_clk);
|
||||||
i_gpio.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_plic.rst_i(s_rst);
|
||||||
i_master.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);
|
SC_THREAD(gen_reset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ public:
|
||||||
plic i_plic;
|
plic i_plic;
|
||||||
sc_core::sc_signal<sc_core::sc_time> s_clk;
|
sc_core::sc_signal<sc_core::sc_time> s_clk;
|
||||||
sc_core::sc_signal<bool> s_rst;
|
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);
|
simple_system(sc_core::sc_module_name nm);
|
||||||
protected:
|
protected:
|
||||||
|
|
|
@ -21,43 +21,266 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "test_initiator.h"
|
#include "test_initiator.h"
|
||||||
#include <sr_report/sr_report.h>
|
#include <sysc/report.h>
|
||||||
#include <sysc/utilities.h>
|
#include <sysc/utilities.h>
|
||||||
#include <array>
|
#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)
|
test_initiator::test_initiator(sc_core::sc_module_name nm)
|
||||||
: sc_core::sc_module(nm)
|
: sc_core::sc_module(nm)
|
||||||
, NAMED(intor)
|
, NAMED(intor)
|
||||||
, NAMED(rst_i)
|
, 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());
|
if(rst_i.read()==false) wait(rst_i.posedge_event());
|
||||||
wait(rst_i.negedge_event());
|
wait(rst_i.negedge_event());
|
||||||
wait(10_ns);
|
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;
|
tlm::tlm_generic_payload gp;
|
||||||
std::array<uint8_t, 4> data;
|
std::array<uint8_t, 4> data;
|
||||||
srInfo()("group", "comm")("read access");
|
data[3] = 0xff & dat>>24;
|
||||||
gp.set_command(tlm::TLM_READ_COMMAND);
|
data[2] = 0xff & dat>>16;
|
||||||
// gp.set_address(0x10012000);
|
data[1] = 0xff & dat>>8;
|
||||||
gp.set_address(0xc000004);
|
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_ptr(data.data());
|
||||||
gp.set_data_length(data.size());
|
gp.set_data_length(data.size());
|
||||||
gp.set_streaming_width(4);
|
gp.set_streaming_width(4);
|
||||||
sc_core::sc_time delay;
|
sc_core::sc_time delay;
|
||||||
intor->b_transport(gp, delay);
|
intor->b_transport(gp, delay);
|
||||||
wait(10_ns);
|
|
||||||
srWarn()("group", "comm")("write access");
|
if ( gp.get_response_status() != tlm::TLM_OK_RESPONSE ) {
|
||||||
gp.set_command(tlm::TLM_WRITE_COMMAND);
|
throw std::exception();
|
||||||
gp.set_address(0x10012000);
|
}
|
||||||
data[0]=0xA5;
|
}
|
||||||
|
|
||||||
|
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);
|
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 */
|
} /* namespace sysc */
|
||||||
|
|
|
@ -32,11 +32,21 @@ class test_initiator: public sc_core::sc_module {
|
||||||
public:
|
public:
|
||||||
SC_HAS_PROCESS(test_initiator);
|
SC_HAS_PROCESS(test_initiator);
|
||||||
tlm_utils::simple_initiator_socket<test_initiator, 32> intor;
|
tlm_utils::simple_initiator_socket<test_initiator, 32> intor;
|
||||||
|
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;
|
sc_core::sc_in<bool> rst_i;
|
||||||
test_initiator(sc_core::sc_module_name nm);
|
test_initiator(sc_core::sc_module_name nm);
|
||||||
protected:
|
protected:
|
||||||
void run();
|
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 */
|
} /* namespace sysc */
|
||||||
|
|
Loading…
Reference in New Issue