273 lines
7.8 KiB
C++
273 lines
7.8 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// 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 <array>
|
|
#include <scc/report.h>
|
|
#include <scc/utilities.h>
|
|
|
|
// 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(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;
|
|
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<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_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 */
|