Merge remote-tracking branch 'upstream/main' into graphics
This commit is contained in:
1
generators/bar-fetchers
Submodule
1
generators/bar-fetchers
Submodule
Submodule generators/bar-fetchers added at 3a33d818ae
Submodule generators/boom updated: 1b1f210bcf...1a153d4974
@@ -1,13 +1,44 @@
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <riscv/sim.h>
|
||||
#include <riscv/mmu.h>
|
||||
#include <riscv/encoding.h>
|
||||
#include <vpi_user.h>
|
||||
#include <svdpi.h>
|
||||
#include <sstream>
|
||||
#include <set>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if __has_include ("cospike_dtm.h")
|
||||
#define COSPIKE_DTM
|
||||
#include "testchip_dtm.h"
|
||||
extern testchip_dtm_t* dtm;
|
||||
bool spike_loadarch_done = false;
|
||||
#endif
|
||||
|
||||
#if __has_include ("mm.h")
|
||||
#define COSPIKE_SIMDRAM
|
||||
#include "mm.h"
|
||||
extern std::map<long long int, backing_data_t> backing_mem_data;
|
||||
#endif
|
||||
|
||||
#define CLINT_BASE (0x2000000)
|
||||
#define CLINT_SIZE (0x1000)
|
||||
#define CLINT_SIZE (0x10000)
|
||||
#define UART_BASE (0x54000000)
|
||||
#define UART_SIZE (0x1000)
|
||||
#define PLIC_BASE (0xc000000)
|
||||
#define PLIC_SIZE (0x4000000)
|
||||
|
||||
#define COSPIKE_PRINTF(...) { \
|
||||
printf(__VA_ARGS__); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
}
|
||||
|
||||
typedef struct system_info_t {
|
||||
std::string isa;
|
||||
@@ -16,14 +47,37 @@ typedef struct system_info_t {
|
||||
uint64_t mem0_size;
|
||||
int nharts;
|
||||
std::vector<char> bootrom;
|
||||
std::string priv;
|
||||
};
|
||||
|
||||
class read_override_device_t : public abstract_device_t {
|
||||
public:
|
||||
read_override_device_t(std::string n, reg_t sz) : was_read_from(false), size(sz), name(n) { };
|
||||
virtual bool load(reg_t addr, size_t len, uint8_t* bytes) override {
|
||||
if (addr + len > size) return false;
|
||||
COSPIKE_PRINTF("Read from device %s at %lx\n", name.c_str(), addr);
|
||||
was_read_from = true;
|
||||
return true;
|
||||
}
|
||||
virtual bool store(reg_t addr, size_t len, const uint8_t* bytes) override {
|
||||
COSPIKE_PRINTF("Store to device %s at %lx\n", name.c_str(), addr);
|
||||
return (addr + len <= size);
|
||||
}
|
||||
bool was_read_from;
|
||||
private:
|
||||
reg_t size;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
system_info_t* info = NULL;
|
||||
sim_t* sim = NULL;
|
||||
bool cospike_debug;
|
||||
reg_t tohost_addr = 0;
|
||||
reg_t fromhost_addr = 0;
|
||||
reg_t cospike_timeout = 0;
|
||||
std::set<reg_t> magic_addrs;
|
||||
cfg_t* cfg;
|
||||
std::vector<std::shared_ptr<read_override_device_t>> read_override_devices;
|
||||
|
||||
static std::vector<std::pair<reg_t, mem_t*>> make_mems(const std::vector<mem_cfg_t> &layout)
|
||||
{
|
||||
@@ -35,14 +89,16 @@ static std::vector<std::pair<reg_t, mem_t*>> make_mems(const std::vector<mem_cfg
|
||||
return mems;
|
||||
}
|
||||
|
||||
extern "C" void cospike_set_sysinfo(char* isa, int pmpregions,
|
||||
extern "C" void cospike_set_sysinfo(char* isa, char* priv, int pmpregions,
|
||||
long long int mem0_base, long long int mem0_size,
|
||||
int nharts,
|
||||
char* bootrom
|
||||
) {
|
||||
if (!info) {
|
||||
info = new system_info_t;
|
||||
info->isa = std::string(isa);
|
||||
// technically the targets aren't zicntr compliant, but they implement the zicntr registers
|
||||
info->isa = std::string(isa) + "_zicntr";
|
||||
info->priv = std::string(priv);
|
||||
info->pmpregions = pmpregions;
|
||||
info->mem0_base = mem0_base;
|
||||
info->mem0_size = mem0_size;
|
||||
@@ -64,11 +120,13 @@ extern "C" void cospike_cosim(long long int cycle,
|
||||
int raise_exception,
|
||||
int raise_interrupt,
|
||||
unsigned long long int cause,
|
||||
unsigned long long int wdata)
|
||||
unsigned long long int wdata,
|
||||
int priv)
|
||||
{
|
||||
assert(info);
|
||||
if (!sim) {
|
||||
printf("Configuring spike cosim\n");
|
||||
|
||||
if (unlikely(!sim)) {
|
||||
COSPIKE_PRINTF("Configuring spike cosim\n");
|
||||
std::vector<mem_cfg_t> mem_cfg;
|
||||
std::vector<size_t> hartids;
|
||||
mem_cfg.push_back(mem_cfg_t(info->mem0_base, info->mem0_size));
|
||||
@@ -78,7 +136,7 @@ extern "C" void cospike_cosim(long long int cycle,
|
||||
cfg = new cfg_t(std::make_pair(0, 0),
|
||||
nullptr,
|
||||
info->isa.c_str(),
|
||||
"MSU",
|
||||
info->priv.c_str(),
|
||||
"vlen:128,elen:64",
|
||||
false,
|
||||
endianness_little,
|
||||
@@ -91,34 +149,48 @@ extern "C" void cospike_cosim(long long int cycle,
|
||||
|
||||
std::vector<std::pair<reg_t, mem_t*>> mems = make_mems(cfg->mem_layout());
|
||||
|
||||
rom_device_t *boot_rom = new rom_device_t(info->bootrom);
|
||||
mem_t *boot_addr_reg = new mem_t(0x1000);
|
||||
size_t default_boot_rom_size = 0x10000;
|
||||
size_t default_boot_rom_addr = 0x10000;
|
||||
assert(info->bootrom.size() < default_boot_rom_size);
|
||||
info->bootrom.resize(default_boot_rom_size);
|
||||
|
||||
std::shared_ptr<rom_device_t> boot_rom = std::make_shared<rom_device_t>(info->bootrom);
|
||||
std::shared_ptr<mem_t> boot_addr_reg = std::make_shared<mem_t>(0x1000);
|
||||
uint64_t default_boot_addr = 0x80000000;
|
||||
boot_addr_reg->store(0, 8, (const uint8_t*)(&default_boot_addr));
|
||||
boot_addr_reg.get()->store(0, 8, (const uint8_t*)(&default_boot_addr));
|
||||
|
||||
// Don't actually build a clint
|
||||
mem_t* clint_mem = new mem_t(CLINT_SIZE);
|
||||
std::shared_ptr<read_override_device_t> clint = std::make_shared<read_override_device_t>("clint", CLINT_SIZE);
|
||||
std::shared_ptr<read_override_device_t> uart = std::make_shared<read_override_device_t>("uart", UART_SIZE);
|
||||
std::shared_ptr<read_override_device_t> plic = std::make_shared<read_override_device_t>("plic", PLIC_SIZE);
|
||||
|
||||
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices;
|
||||
read_override_devices.push_back(clint);
|
||||
read_override_devices.push_back(uart);
|
||||
read_override_devices.push_back(plic);
|
||||
|
||||
std::vector<std::pair<reg_t, std::shared_ptr<abstract_device_t>>> devices;
|
||||
// The device map is hardcoded here for now
|
||||
plugin_devices.push_back(std::pair(0x4000, boot_addr_reg));
|
||||
plugin_devices.push_back(std::pair(0x10000, boot_rom));
|
||||
plugin_devices.push_back(std::pair(CLINT_BASE, clint_mem));
|
||||
devices.push_back(std::pair(0x4000, boot_addr_reg));
|
||||
devices.push_back(std::pair(default_boot_rom_addr, boot_rom));
|
||||
devices.push_back(std::pair(CLINT_BASE, clint));
|
||||
devices.push_back(std::pair(UART_BASE, uart));
|
||||
devices.push_back(std::pair(PLIC_BASE, plic));
|
||||
|
||||
s_vpi_vlog_info vinfo;
|
||||
if (!vpi_get_vlog_info(&vinfo))
|
||||
abort();
|
||||
std::vector<std::string> htif_args;
|
||||
bool in_permissive = false;
|
||||
bool cospike_debug = false;
|
||||
cospike_debug = false;
|
||||
for (int i = 1; i < vinfo.argc; i++) {
|
||||
std::string arg(vinfo.argv[i]);
|
||||
if (arg == "+permissive") {
|
||||
in_permissive = true;
|
||||
} else if (arg == "+permissive-off") {
|
||||
in_permissive = false;
|
||||
} else if (arg == "+cospike_debug") {
|
||||
} else if (arg == "+cospike_debug" || arg == "+cospike-debug") {
|
||||
cospike_debug = true;
|
||||
} else if (arg.find("+cospike-timeout=") == 0) {
|
||||
cospike_timeout = strtoull(arg.substr(17).c_str(), 0, 10);
|
||||
} else if (!in_permissive) {
|
||||
htif_args.push_back(arg);
|
||||
}
|
||||
@@ -136,14 +208,17 @@ extern "C" void cospike_cosim(long long int cycle,
|
||||
.support_impebreak = true
|
||||
};
|
||||
|
||||
printf("%s\n", info->isa.c_str());
|
||||
COSPIKE_PRINTF("isa string: %s\n", info->isa.c_str());
|
||||
COSPIKE_PRINTF("htif args: ");
|
||||
for (int i = 0; i < htif_args.size(); i++) {
|
||||
printf("%s\n", htif_args[i].c_str());
|
||||
COSPIKE_PRINTF("%s", htif_args[i].c_str());
|
||||
}
|
||||
COSPIKE_PRINTF("\n");
|
||||
|
||||
std::vector<const device_factory_t*> plugin_device_factories;
|
||||
sim = new sim_t(cfg, false,
|
||||
mems,
|
||||
plugin_devices,
|
||||
plugin_device_factories,
|
||||
htif_args,
|
||||
dm_config,
|
||||
nullptr,
|
||||
@@ -152,107 +227,274 @@ extern "C" void cospike_cosim(long long int cycle,
|
||||
false,
|
||||
nullptr
|
||||
);
|
||||
for (auto &it : devices)
|
||||
sim->add_device(it.first, it.second);
|
||||
|
||||
#ifdef COSPIKE_SIMDRAM
|
||||
// match sim_t's backing memory with the SimDRAM memory
|
||||
bus_t temp_mem_bus;
|
||||
for (auto& pair : mems) temp_mem_bus.add_device(pair.first, pair.second);
|
||||
|
||||
for (auto& pair : backing_mem_data) {
|
||||
size_t base = pair.first;
|
||||
size_t size = pair.second.size;
|
||||
COSPIKE_PRINTF("Matching spike memory initial state for region %lx-%lx\n", base, base + size);
|
||||
if (!temp_mem_bus.store(base, size, pair.second.data)) {
|
||||
COSPIKE_PRINTF("Error, unable to match memory at address %lx\n", base);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sim->configure_log(true, true);
|
||||
// Use our own reset vector
|
||||
for (int i = 0; i < info->nharts; i++) {
|
||||
// Use our own reset vector
|
||||
sim->get_core(hartid)->get_state()->pc = 0x10040;
|
||||
// Set MMU to support up to sv39, as our normal hw configs do
|
||||
sim->get_core(hartid)->set_impl(IMPL_MMU_SV48, false);
|
||||
sim->get_core(hartid)->set_impl(IMPL_MMU_SV57, false);
|
||||
|
||||
// HACKS: Our processor's don't implement zicntr fully, they don't provide time
|
||||
sim->get_core(hartid)->get_state()->csrmap.erase(CSR_TIME);
|
||||
}
|
||||
sim->set_debug(cospike_debug);
|
||||
printf("Setting up htif for spike cosim\n");
|
||||
sim->set_histogram(true);
|
||||
sim->set_procs_debug(cospike_debug);
|
||||
COSPIKE_PRINTF("Setting up htif for spike cosim\n");
|
||||
((htif_t*)sim)->start();
|
||||
printf("Spike cosim started\n");
|
||||
COSPIKE_PRINTF("Spike cosim started\n");
|
||||
tohost_addr = ((htif_t*)sim)->get_tohost_addr();
|
||||
fromhost_addr = ((htif_t*)sim)->get_fromhost_addr();
|
||||
printf("Tohost : %lx\n", tohost_addr);
|
||||
printf("Fromhost: %lx\n", fromhost_addr);
|
||||
COSPIKE_PRINTF("Tohost : %lx\n", tohost_addr);
|
||||
COSPIKE_PRINTF("Fromhost: %lx\n", fromhost_addr);
|
||||
COSPIKE_PRINTF("BootROM base : %lx\n", default_boot_rom_addr);
|
||||
COSPIKE_PRINTF("BootROM size : %lx\n", boot_rom->contents().size());
|
||||
COSPIKE_PRINTF("Memory base : %lx\n", info->mem0_base);
|
||||
COSPIKE_PRINTF("Memory size : %lx\n", info->mem0_size);
|
||||
}
|
||||
|
||||
if (priv & 0x4) { // debug
|
||||
return;
|
||||
}
|
||||
|
||||
if (cospike_timeout && cycle > cospike_timeout) {
|
||||
if (sim) {
|
||||
COSPIKE_PRINTF("Cospike reached timeout cycles = %ld, terminating\n", cospike_timeout);
|
||||
delete sim;
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
processor_t* p = sim->get_core(hartid);
|
||||
state_t* s = p->get_state();
|
||||
#ifdef COSPIKE_DTM
|
||||
if (dtm && dtm->loadarch_done && !spike_loadarch_done) {
|
||||
COSPIKE_PRINTF("Restoring spike state from testchip_dtm loadarch\n");
|
||||
// copy the loadarch state into the cosim
|
||||
loadarch_state_t &ls = dtm->loadarch_state[hartid];
|
||||
s->pc = ls.pc;
|
||||
s->prv = ls.prv;
|
||||
s->csrmap[CSR_MSTATUS]->write(s->csrmap[CSR_MSTATUS]->read() | MSTATUS_VS | MSTATUS_XS | MSTATUS_FS);
|
||||
#define RESTORE(CSRID, csr) s->csrmap[CSRID]->write(ls.csr);
|
||||
RESTORE(CSR_FCSR , fcsr);
|
||||
RESTORE(CSR_VSTART , vstart);
|
||||
RESTORE(CSR_VXSAT , vxsat);
|
||||
RESTORE(CSR_VXRM , vxrm);
|
||||
RESTORE(CSR_VCSR , vcsr);
|
||||
RESTORE(CSR_VTYPE , vtype);
|
||||
RESTORE(CSR_STVEC , stvec);
|
||||
RESTORE(CSR_SSCRATCH , sscratch);
|
||||
RESTORE(CSR_SEPC , sepc);
|
||||
RESTORE(CSR_SCAUSE , scause);
|
||||
RESTORE(CSR_STVAL , stval);
|
||||
RESTORE(CSR_SATP , satp);
|
||||
RESTORE(CSR_MSTATUS , mstatus);
|
||||
RESTORE(CSR_MEDELEG , medeleg);
|
||||
RESTORE(CSR_MIDELEG , mideleg);
|
||||
RESTORE(CSR_MIE , mie);
|
||||
RESTORE(CSR_MTVEC , mtvec);
|
||||
RESTORE(CSR_MSCRATCH , mscratch);
|
||||
RESTORE(CSR_MEPC , mepc);
|
||||
RESTORE(CSR_MCAUSE , mcause);
|
||||
RESTORE(CSR_MTVAL , mtval);
|
||||
RESTORE(CSR_MIP , mip);
|
||||
RESTORE(CSR_MCYCLE , mcycle);
|
||||
RESTORE(CSR_MINSTRET , minstret);
|
||||
if (ls.VLEN != p->VU.VLEN) {
|
||||
COSPIKE_PRINTF("VLEN mismatch loadarch: $d != spike: $d\n", ls.VLEN, p->VU.VLEN);
|
||||
abort();
|
||||
}
|
||||
if (ls.ELEN != p->VU.ELEN) {
|
||||
COSPIKE_PRINTF("ELEN mismatch loadarch: $d != spike: $d\n", ls.ELEN, p->VU.ELEN);
|
||||
abort();
|
||||
}
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
s->XPR.write(i, ls.XPR[i]);
|
||||
s->FPR.write(i, { (uint64_t)ls.FPR[i], (uint64_t)-1 });
|
||||
memcpy(p->VU.reg_file + i * ls.VLEN / 8, ls.VPR[i], ls.VLEN / 8);
|
||||
}
|
||||
spike_loadarch_done = true;
|
||||
p->clear_waiting_for_interrupt();
|
||||
}
|
||||
#endif
|
||||
uint64_t s_pc = s->pc;
|
||||
uint64_t interrupt_cause = cause & 0x7FFFFFFFFFFFFFFF;
|
||||
bool ssip_interrupt = interrupt_cause == 0x1;
|
||||
bool msip_interrupt = interrupt_cause == 0x3;
|
||||
bool stip_interrupt = interrupt_cause == 0x5;
|
||||
bool mtip_interrupt = interrupt_cause == 0x7;
|
||||
bool debug_interrupt = interrupt_cause == 0xe;
|
||||
if (raise_interrupt) {
|
||||
printf("%d interrupt %lx\n", cycle, cause);
|
||||
uint64_t interrupt_cause = cause & 0x7FFFFFFFFFFFFFFF;
|
||||
if (interrupt_cause == 3) {
|
||||
COSPIKE_PRINTF("%d interrupt %lx\n", cycle, cause);
|
||||
|
||||
if (ssip_interrupt || stip_interrupt) {
|
||||
// do nothing
|
||||
} else if (msip_interrupt) {
|
||||
s->mip->backdoor_write_with_mask(MIP_MSIP, MIP_MSIP);
|
||||
} else if (mtip_interrupt) {
|
||||
s->mip->backdoor_write_with_mask(MIP_MTIP, MIP_MTIP);
|
||||
} else if (debug_interrupt) {
|
||||
return;
|
||||
} else {
|
||||
printf("Unknown interrupt %lx\n", interrupt_cause);
|
||||
COSPIKE_PRINTF("Unknown interrupt %lx\n", interrupt_cause);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
if (raise_exception)
|
||||
printf("%d exception %lx\n", cycle, cause);
|
||||
COSPIKE_PRINTF("%d exception %lx\n", cycle, cause);
|
||||
if (valid) {
|
||||
printf("%d Cosim: %lx", cycle, iaddr);
|
||||
if (has_wdata) {
|
||||
printf(" %lx", wdata);
|
||||
}
|
||||
printf("\n");
|
||||
p->clear_waiting_for_interrupt();
|
||||
COSPIKE_PRINTF("%d Cosim: %lx", cycle, iaddr);
|
||||
// if (has_wdata) {
|
||||
// COSPIKE_PRINTF(" s: %lx", wdata);
|
||||
// }
|
||||
COSPIKE_PRINTF("\n");
|
||||
}
|
||||
if (valid || raise_interrupt || raise_exception)
|
||||
if (valid || raise_interrupt || raise_exception) {
|
||||
p->clear_waiting_for_interrupt();
|
||||
for (auto& e : read_override_devices) e.get()->was_read_from = false;
|
||||
p->step(1);
|
||||
if (unlikely(cospike_debug)) {
|
||||
COSPIKE_PRINTF("spike pc is %lx\n", s->pc);
|
||||
COSPIKE_PRINTF("spike mstatus is %lx\n", s->mstatus->read());
|
||||
COSPIKE_PRINTF("spike mip is %lx\n", s->mip->read());
|
||||
COSPIKE_PRINTF("spike mie is %lx\n", s->mie->read());
|
||||
COSPIKE_PRINTF("spike wfi state is %d\n", p->is_waiting_for_interrupt());
|
||||
}
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
if (valid && !raise_exception) {
|
||||
if (s_pc != iaddr) {
|
||||
printf("%d PC mismatch %lx != %lx\n", cycle, s_pc, iaddr);
|
||||
COSPIKE_PRINTF("%d PC mismatch spike %llx != DUT %llx\n", cycle, s_pc, iaddr);
|
||||
if (unlikely(cospike_debug)) {
|
||||
COSPIKE_PRINTF("spike mstatus is %lx\n", s->mstatus->read());
|
||||
COSPIKE_PRINTF("spike mcause is %lx\n", s->mcause->read());
|
||||
COSPIKE_PRINTF("spike mtval is %lx\n" , s->mtval->read());
|
||||
COSPIKE_PRINTF("spike mtinst is %lx\n", s->mtinst->read());
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Try to remember magic_mem addrs, and ignore these in the future
|
||||
|
||||
auto& mem_write = s->log_mem_write;
|
||||
if (!mem_write.empty() && tohost_addr && std::get<0>(mem_write[0]) == tohost_addr) {
|
||||
reg_t wdata = std::get<1>(mem_write[0]);
|
||||
if (wdata >= info->mem0_base && wdata < (info->mem0_base + info->mem0_size)) {
|
||||
printf("Probable magic mem %x\n", wdata);
|
||||
magic_addrs.insert(wdata);
|
||||
auto& log = s->log_reg_write;
|
||||
auto& mem_read = s->log_mem_read;
|
||||
|
||||
for (auto memwrite : mem_write) {
|
||||
reg_t waddr = std::get<0>(memwrite);
|
||||
uint64_t w_data = std::get<1>(memwrite);
|
||||
if ((waddr == CLINT_BASE + 4*hartid) && w_data == 0) {
|
||||
s->mip->backdoor_write_with_mask(MIP_MSIP, 0);
|
||||
}
|
||||
if ((waddr == CLINT_BASE + 0x4000 + 4*hartid)) {
|
||||
s->mip->backdoor_write_with_mask(MIP_MTIP, 0);
|
||||
}
|
||||
// Try to remember magic_mem addrs, and ignore these in the future
|
||||
if ( waddr == tohost_addr && w_data >= info->mem0_base && w_data < (info->mem0_base + info->mem0_size)) {
|
||||
COSPIKE_PRINTF("Probable magic mem %lx\n", w_data);
|
||||
magic_addrs.insert(w_data);
|
||||
}
|
||||
}
|
||||
|
||||
if (has_wdata) {
|
||||
auto& log = s->log_reg_write;
|
||||
auto& mem_read = s->log_mem_read;
|
||||
bool scalar_wb = false;
|
||||
bool vector_wb = false;
|
||||
uint32_t vector_cnt = 0;
|
||||
|
||||
for (auto ®write : log) {
|
||||
|
||||
//TODO: scaling to multi issue reads?
|
||||
reg_t mem_read_addr = mem_read.empty() ? 0 : std::get<0>(mem_read[0]);
|
||||
for (auto regwrite : log) {
|
||||
int rd = regwrite.first >> 4;
|
||||
int type = regwrite.first & 0xf;
|
||||
// 0 => int
|
||||
// 1 => fp
|
||||
// 2 => vec
|
||||
// 3 => vec hint
|
||||
// 4 => csr
|
||||
if ((rd != 0 && type == 0) || type == 1) {
|
||||
// Override reads from some CSRs
|
||||
uint64_t csr_addr = (insn >> 20) & 0xfff;
|
||||
bool csr_read = (insn & 0x7f) == 0x73;
|
||||
if (csr_read) printf("CSR read %lx\n", csr_addr);
|
||||
if (csr_read && (
|
||||
(csr_addr == 0x301) || // misa
|
||||
(csr_addr == 0xf13) || // mimpid
|
||||
(csr_addr == 0xf12) || // marchid
|
||||
(csr_addr == 0xf11) || // mvendorid
|
||||
(csr_addr == 0xb00) || // mcycle
|
||||
(csr_addr == 0xb02) || // minstret
|
||||
(csr_addr >= 0x3b0 && csr_addr <= 0x3ef) // pmpaddr
|
||||
)) {
|
||||
printf("CSR override\n");
|
||||
s->XPR.write(rd, wdata);
|
||||
} else if (!mem_read.empty() && ((magic_addrs.count(mem_read_addr) ||
|
||||
(tohost_addr && mem_read_addr == tohost_addr) ||
|
||||
(fromhost_addr && mem_read_addr == fromhost_addr) ||
|
||||
(CLINT_BASE <= mem_read_addr && mem_read_addr < (CLINT_BASE + CLINT_SIZE))
|
||||
))) {
|
||||
// Don't check reads from tohost, reads from magic memory, or reads from clint
|
||||
// Technically this could be buggy because log_mem_read only reports vaddrs, but
|
||||
// no software ever should access tohost/fromhost/clint with vaddrs anyways
|
||||
printf("Read override %lx\n", mem_read_addr);
|
||||
s->XPR.write(rd, wdata);
|
||||
} else if (wdata != regwrite.second.v[0]) {
|
||||
printf("%d wdata mismatch reg %d %lx != %lx\n", cycle, rd, regwrite.second.v[0], wdata);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int rd = regwrite.first >> 4;
|
||||
int type = regwrite.first & 0xf;
|
||||
|
||||
// 0 => int
|
||||
// 1 => fp
|
||||
// 2 => vec
|
||||
// 3 => vec hint
|
||||
// 4 => csr
|
||||
bool device_read = false;
|
||||
for (auto& e : read_override_devices) if (e.get()->was_read_from) device_read = true;
|
||||
|
||||
bool lr_read = ((insn & MASK_LR_D) == MATCH_LR_D) || ((insn & MASK_LR_W) == MATCH_LR_W);
|
||||
bool sc_read = ((insn & MASK_SC_D) == MATCH_SC_D) || ((insn & MASK_SC_W) == MATCH_SC_W);
|
||||
|
||||
bool ignore_read = sc_read || (!mem_read.empty() &&
|
||||
(magic_addrs.count(mem_read_addr) ||
|
||||
device_read ||
|
||||
lr_read ||
|
||||
(tohost_addr && mem_read_addr == tohost_addr) ||
|
||||
(fromhost_addr && mem_read_addr == fromhost_addr)));
|
||||
// check the type is compliant with writeback first
|
||||
if ((type == 0 || type == 1))
|
||||
scalar_wb = true;
|
||||
if (type == 2) {
|
||||
vector_wb = true;
|
||||
}
|
||||
if (type == 3) continue;
|
||||
|
||||
|
||||
if ((rd != 0 && type == 0) || type == 1) {
|
||||
// Override reads from some CSRs
|
||||
uint64_t csr_addr = (insn >> 20) & 0xfff;
|
||||
bool csr_read = (insn & 0x7f) == 0x73;
|
||||
if (csr_read)
|
||||
COSPIKE_PRINTF("CSR read %lx\n", csr_addr);
|
||||
if (csr_read && ((csr_addr == 0x301) || // misa
|
||||
(csr_addr == 0x306) || // mcounteren
|
||||
(csr_addr == 0xf13) || // mimpid
|
||||
(csr_addr == 0xf12) || // marchid
|
||||
(csr_addr == 0xf11) || // mvendorid
|
||||
(csr_addr == 0xb00) || // mcycle
|
||||
(csr_addr == 0xb02) || // minstret
|
||||
(csr_addr == 0xc00) || // cycle
|
||||
(csr_addr == 0xc01) || // time
|
||||
(csr_addr == 0xc02) || // instret
|
||||
(csr_addr >= 0x7a0 && csr_addr <= 0x7aa) || // debug trigger registers
|
||||
(csr_addr >= 0x3b0 && csr_addr <= 0x3ef) // pmpaddr
|
||||
)) {
|
||||
COSPIKE_PRINTF("CSR override\n");
|
||||
s->XPR.write(rd, wdata);
|
||||
} else if (ignore_read) {
|
||||
// Don't check reads from tohost, reads from magic memory, or reads
|
||||
// from clint Technically this could be buggy because log_mem_read
|
||||
// only reports vaddrs, but no software ever should access
|
||||
// tohost/fromhost/clint with vaddrs anyways
|
||||
COSPIKE_PRINTF("Read override %lx = %lx\n", mem_read_addr, wdata);
|
||||
s->XPR.write(rd, wdata);
|
||||
} else if (wdata != regwrite.second.v[0]) {
|
||||
COSPIKE_PRINTF("%d wdata mismatch reg %d %lx != %lx\n", cycle, rd,
|
||||
regwrite.second.v[0], wdata);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO FIX: Rocketchip TracedInstruction.wdata should be Valid(UInt)
|
||||
// if (scalar_wb ^ has_wdata) {
|
||||
// COSPIKE_PRINTF("Scalar wdata behavior divergence between spike and DUT\n");
|
||||
// exit(-1);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
@@ -1,394 +0,0 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
// See LICENSE.Berkeley for license details.
|
||||
|
||||
#if VM_TRACE
|
||||
#include <memory>
|
||||
#if CY_FST_TRACE
|
||||
#include "verilated_fst_c.h"
|
||||
#else
|
||||
#include "verilated.h"
|
||||
#include "verilated_vcd_c.h"
|
||||
#endif // CY_FST_TRACE
|
||||
#endif // VM_TRACE
|
||||
#include <fesvr/dtm.h>
|
||||
#include <fesvr/tsi.h>
|
||||
#include "remote_bitbang.h"
|
||||
#include <iostream>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <getopt.h>
|
||||
|
||||
// For option parsing, which is split across this file, Verilog, and
|
||||
// FESVR's HTIF, a few external files must be pulled in. The list of
|
||||
// files and what they provide is enumerated:
|
||||
//
|
||||
// $RISCV/include/fesvr/htif.h:
|
||||
// defines:
|
||||
// - HTIF_USAGE_OPTIONS
|
||||
// - HTIF_LONG_OPTIONS_OPTIND
|
||||
// - HTIF_LONG_OPTIONS
|
||||
// $(ROCKETCHIP_DIR)/generated-src(-debug)?/$(CONFIG).plusArgs:
|
||||
// defines:
|
||||
// - PLUSARG_USAGE_OPTIONS
|
||||
// variables:
|
||||
// - static const char * verilog_plusargs
|
||||
|
||||
extern tsi_t* tsi;
|
||||
extern dtm_t* dtm;
|
||||
extern remote_bitbang_t * jtag;
|
||||
|
||||
static uint64_t trace_count = 0;
|
||||
bool verbose = false;
|
||||
bool done_reset = false;
|
||||
|
||||
void handle_sigterm(int sig)
|
||||
{
|
||||
dtm->stop();
|
||||
}
|
||||
|
||||
double sc_time_stamp()
|
||||
{
|
||||
return trace_count;
|
||||
}
|
||||
|
||||
static void usage(const char * program_name)
|
||||
{
|
||||
printf("Usage: %s [EMULATOR OPTION]... [VERILOG PLUSARG]... [HOST OPTION]... BINARY [TARGET OPTION]...\n",
|
||||
program_name);
|
||||
fputs("\
|
||||
Run a BINARY on the Rocket Chip emulator.\n\
|
||||
\n\
|
||||
Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
\n\
|
||||
EMULATOR OPTIONS\n\
|
||||
-c, --cycle-count Print the cycle count before exiting\n\
|
||||
+cycle-count\n\
|
||||
-h, --help Display this help and exit\n\
|
||||
-m, --max-cycles=CYCLES Kill the emulation after CYCLES\n\
|
||||
+max-cycles=CYCLES\n\
|
||||
-s, --seed=SEED Use random number seed SEED\n\
|
||||
-r, --rbb-port=PORT Use PORT for remote bit bang (with OpenOCD and GDB) \n\
|
||||
If not specified, a random port will be chosen\n\
|
||||
automatically.\n\
|
||||
-V, --verbose Enable all Chisel printfs (cycle-by-cycle info)\n\
|
||||
+verbose\n\
|
||||
", stdout);
|
||||
#if VM_TRACE == 0
|
||||
fputs("\
|
||||
\n\
|
||||
EMULATOR DEBUG OPTIONS (only supported in debug build -- try `make debug`)\n",
|
||||
stdout);
|
||||
#endif
|
||||
fputs("\
|
||||
-v, --vcd=FILE, Write vcd trace to FILE (or '-' for stdout)\n\
|
||||
-x, --dump-start=CYCLE Start VCD tracing at CYCLE\n\
|
||||
+dump-start\n\
|
||||
", stdout);
|
||||
fputs("\n" PLUSARG_USAGE_OPTIONS, stdout);
|
||||
fputs("\n" HTIF_USAGE_OPTIONS, stdout);
|
||||
printf("\n"
|
||||
"EXAMPLES\n"
|
||||
" - run a bare metal test:\n"
|
||||
" %s $RISCV/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-add\n"
|
||||
" - run a bare metal test showing cycle-by-cycle information:\n"
|
||||
" %s +verbose $RISCV/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-add 2>&1 | spike-dasm\n"
|
||||
#if VM_TRACE
|
||||
" - run a bare metal test to generate a VCD waveform:\n"
|
||||
" %s -v rv64ui-p-add.vcd $RISCV/riscv64-unknown-elf/share/riscv-tests/isa/rv64ui-p-add\n"
|
||||
#endif
|
||||
" - run an ELF (you wrote, called 'hello') using the proxy kernel:\n"
|
||||
" %s pk hello\n",
|
||||
program_name, program_name, program_name
|
||||
#if VM_TRACE
|
||||
, program_name
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
unsigned random_seed = (unsigned)time(NULL) ^ (unsigned)getpid();
|
||||
uint64_t max_cycles = -1;
|
||||
int ret = 0;
|
||||
bool print_cycles = false;
|
||||
// Port numbers are 16 bit unsigned integers.
|
||||
uint16_t rbb_port = 0;
|
||||
#if VM_TRACE
|
||||
const char* vcdfile_name = NULL;
|
||||
FILE * vcdfile = NULL;
|
||||
uint64_t start = 0;
|
||||
#endif
|
||||
int verilog_plusargs_legal = 1;
|
||||
|
||||
int verilated_argc = 1;
|
||||
char** verilated_argv = new char*[argc];
|
||||
verilated_argv[0] = argv[0];
|
||||
|
||||
opterr = 1;
|
||||
|
||||
while (1) {
|
||||
static struct option long_options[] = {
|
||||
{"cycle-count", no_argument, 0, 'c' },
|
||||
{"help", no_argument, 0, 'h' },
|
||||
{"max-cycles", required_argument, 0, 'm' },
|
||||
{"seed", required_argument, 0, 's' },
|
||||
{"rbb-port", required_argument, 0, 'r' },
|
||||
{"verbose", no_argument, 0, 'V' },
|
||||
{"permissive", no_argument, 0, 'p' },
|
||||
{"permissive-off", no_argument, 0, 'o' },
|
||||
#if VM_TRACE
|
||||
{"vcd", required_argument, 0, 'v' },
|
||||
{"dump-start", required_argument, 0, 'x' },
|
||||
#endif
|
||||
HTIF_LONG_OPTIONS
|
||||
};
|
||||
int option_index = 0;
|
||||
#if VM_TRACE
|
||||
int c = getopt_long(argc, argv, "-chm:s:r:v:Vx:po", long_options, &option_index);
|
||||
#else
|
||||
int c = getopt_long(argc, argv, "-chm:s:r:Vpo", long_options, &option_index);
|
||||
#endif
|
||||
if (c == -1) break;
|
||||
retry:
|
||||
switch (c) {
|
||||
// Process long and short EMULATOR options
|
||||
case '?': usage(argv[0]); return 1;
|
||||
case 'c': print_cycles = true; break;
|
||||
case 'h': usage(argv[0]); return 0;
|
||||
case 'm': max_cycles = atoll(optarg); break;
|
||||
case 's': random_seed = atoi(optarg); break;
|
||||
case 'r': rbb_port = atoi(optarg); break;
|
||||
case 'V': verbose = true; break;
|
||||
case 'p': opterr = 0; break;
|
||||
case 'o': opterr = 1; break;
|
||||
#if VM_TRACE
|
||||
case 'v': {
|
||||
vcdfile_name = optarg;
|
||||
vcdfile = strcmp(optarg, "-") == 0 ? stdout : fopen(optarg, "w");
|
||||
if (!vcdfile) {
|
||||
std::cerr << "Unable to open " << optarg << " for VCD write\n";
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'x': start = atoll(optarg); break;
|
||||
#endif
|
||||
// Process legacy '+' EMULATOR arguments by replacing them with
|
||||
// their getopt equivalents
|
||||
case 1: {
|
||||
std::string arg = optarg;
|
||||
if (arg.substr(0, 1) != "+") {
|
||||
optind--;
|
||||
goto done_processing;
|
||||
}
|
||||
if (arg == "+verbose")
|
||||
c = 'V';
|
||||
else if (arg.substr(0, 12) == "+max-cycles=") {
|
||||
c = 'm';
|
||||
optarg = optarg+12;
|
||||
}
|
||||
#if VM_TRACE
|
||||
else if (arg.substr(0, 12) == "+dump-start=") {
|
||||
c = 'x';
|
||||
optarg = optarg+12;
|
||||
}
|
||||
#endif
|
||||
else if (arg.substr(0, 12) == "+cycle-count")
|
||||
c = 'c';
|
||||
else if (arg == "+permissive")
|
||||
{
|
||||
c = 'p';
|
||||
verilated_argv[verilated_argc++] = optarg;
|
||||
}
|
||||
else if (arg == "+permissive-off")
|
||||
{
|
||||
c = 'o';
|
||||
verilated_argv[verilated_argc++] = optarg;
|
||||
}
|
||||
// If we don't find a legacy '+' EMULATOR argument, it still could be
|
||||
// a VERILOG_PLUSARG and not an error.
|
||||
else if (verilog_plusargs_legal) {
|
||||
const char ** plusarg = &verilog_plusargs[0];
|
||||
int legal_verilog_plusarg = 0;
|
||||
while (*plusarg && (legal_verilog_plusarg == 0)){
|
||||
if (arg.substr(1, strlen(*plusarg)) == *plusarg) {
|
||||
legal_verilog_plusarg = 1;
|
||||
}
|
||||
plusarg ++;
|
||||
}
|
||||
if (!legal_verilog_plusarg) {
|
||||
verilog_plusargs_legal = 0;
|
||||
} else {
|
||||
c = 'P';
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
// If we STILL don't find a legacy '+' argument, it still could be
|
||||
// an HTIF (HOST) argument and not an error. If this is the case, then
|
||||
// we're done processing EMULATOR and VERILOG arguments.
|
||||
else {
|
||||
static struct option htif_long_options [] = { HTIF_LONG_OPTIONS };
|
||||
struct option * htif_option = &htif_long_options[0];
|
||||
while (htif_option->name) {
|
||||
if (arg.substr(1, strlen(htif_option->name)) == htif_option->name) {
|
||||
optind--;
|
||||
goto done_processing;
|
||||
}
|
||||
htif_option++;
|
||||
}
|
||||
if(opterr) {
|
||||
std::cerr << argv[0] << ": invalid plus-arg (Verilog or HTIF) \""
|
||||
<< arg << "\"\n";
|
||||
c = '?';
|
||||
} else {
|
||||
c = 'P';
|
||||
}
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
case 'P': // Verilog PlusArg, add to the argument list for verilator environment
|
||||
verilated_argv[verilated_argc++] = optarg;
|
||||
break;
|
||||
// Realize that we've hit HTIF (HOST) arguments or error out
|
||||
default:
|
||||
if (c >= HTIF_LONG_OPTIONS_OPTIND) {
|
||||
optind--;
|
||||
goto done_processing;
|
||||
}
|
||||
c = '?';
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
done_processing:
|
||||
if (optind == argc) {
|
||||
std::cerr << "No binary specified for emulator\n";
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Copy remaining HTIF arguments (if any) and the binary file name into the verilator argument stack
|
||||
while (optind < argc) verilated_argv[verilated_argc++] = argv[optind++];
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "using random seed %u\n", random_seed);
|
||||
|
||||
srand(random_seed);
|
||||
srand48(random_seed);
|
||||
|
||||
Verilated::randReset(2);
|
||||
Verilated::commandArgs(verilated_argc, verilated_argv);
|
||||
TEST_HARNESS *tile = new TEST_HARNESS;
|
||||
|
||||
#if VM_TRACE
|
||||
Verilated::traceEverOn(true); // Verilator must compute traced signals
|
||||
#if CY_FST_TRACE
|
||||
std::unique_ptr<VerilatedFstC> tfp(new VerilatedFstC);
|
||||
#else
|
||||
std::unique_ptr<VerilatedVcdFILE> vcdfd(new VerilatedVcdFILE(vcdfile));
|
||||
std::unique_ptr<VerilatedVcdC> tfp(new VerilatedVcdC(vcdfd.get()));
|
||||
#endif // CY_FST_TRACE
|
||||
if (vcdfile_name) {
|
||||
tile->trace(tfp.get(), 99); // Trace 99 levels of hierarchy
|
||||
tfp->open(vcdfile_name);
|
||||
}
|
||||
#endif // VM_TRACE
|
||||
|
||||
// RocketChip currently only supports RBB port 0, so this needs to stay here
|
||||
jtag = new remote_bitbang_t(rbb_port);
|
||||
|
||||
signal(SIGTERM, handle_sigterm);
|
||||
|
||||
bool dump;
|
||||
// start reset off low so a rising edge triggers async reset
|
||||
tile->reset = 0;
|
||||
tile->clock = 0;
|
||||
tile->eval();
|
||||
// reset for several cycles to handle pipelined reset
|
||||
for (int i = 0; i < 100; i++) {
|
||||
tile->reset = 1;
|
||||
tile->clock = 0;
|
||||
tile->eval();
|
||||
#if VM_TRACE
|
||||
dump = tfp && trace_count >= start;
|
||||
if (dump)
|
||||
tfp->dump(static_cast<vluint64_t>(trace_count * 2));
|
||||
#endif
|
||||
tile->clock = 1;
|
||||
tile->eval();
|
||||
#if VM_TRACE
|
||||
if (dump)
|
||||
tfp->dump(static_cast<vluint64_t>(trace_count * 2 + 1));
|
||||
#endif
|
||||
trace_count ++;
|
||||
}
|
||||
tile->reset = 0;
|
||||
done_reset = true;
|
||||
|
||||
do {
|
||||
tile->clock = 0;
|
||||
tile->eval();
|
||||
#if VM_TRACE
|
||||
dump = tfp && trace_count >= start;
|
||||
if (dump)
|
||||
tfp->dump(static_cast<vluint64_t>(trace_count * 2));
|
||||
#endif
|
||||
|
||||
tile->clock = 1;
|
||||
tile->eval();
|
||||
#if VM_TRACE
|
||||
if (dump)
|
||||
tfp->dump(static_cast<vluint64_t>(trace_count * 2 + 1));
|
||||
#endif
|
||||
trace_count++;
|
||||
}
|
||||
// for verilator multithreading. need to do 1 loop before checking if
|
||||
// tsi exists, since tsi is created by verilated thread on the first
|
||||
// serial_tick.
|
||||
while ((!dtm || !dtm->done()) &&
|
||||
(!jtag || !jtag->done()) &&
|
||||
(!tsi || !tsi->done()) &&
|
||||
!tile->io_success && trace_count < max_cycles);
|
||||
|
||||
#if VM_TRACE
|
||||
if (tfp)
|
||||
tfp->close();
|
||||
if (vcdfile)
|
||||
fclose(vcdfile);
|
||||
#endif
|
||||
|
||||
if (dtm && dtm->exit_code())
|
||||
{
|
||||
fprintf(stderr, "*** FAILED *** via dtm (code = %d, seed %d) after %ld cycles\n", dtm->exit_code(), random_seed, trace_count);
|
||||
ret = dtm->exit_code();
|
||||
}
|
||||
else if (tsi && tsi->exit_code())
|
||||
{
|
||||
fprintf(stderr, "*** FAILED *** (code = %d, seed %d) after %ld cycles\n", tsi->exit_code(), random_seed, trace_count);
|
||||
ret = tsi->exit_code();
|
||||
}
|
||||
else if (jtag && jtag->exit_code())
|
||||
{
|
||||
fprintf(stderr, "*** FAILED *** via jtag (code = %d, seed %d) after %ld cycles\n", jtag->exit_code(), random_seed, trace_count);
|
||||
ret = jtag->exit_code();
|
||||
}
|
||||
else if (trace_count == max_cycles)
|
||||
{
|
||||
fprintf(stderr, "*** FAILED *** via trace_count (timeout, seed %d) after %ld cycles\n", random_seed, trace_count);
|
||||
ret = 2;
|
||||
}
|
||||
else if (verbose || print_cycles)
|
||||
{
|
||||
fprintf(stderr, "*** PASSED *** Completed after %ld cycles\n", trace_count);
|
||||
}
|
||||
|
||||
if (dtm) delete dtm;
|
||||
if (tsi) delete tsi;
|
||||
if (jtag) delete jtag;
|
||||
if (tile) delete tile;
|
||||
if (verilated_argv) delete[] verilated_argv;
|
||||
return ret;
|
||||
}
|
||||
@@ -2,13 +2,22 @@
|
||||
#include <riscv/processor.h>
|
||||
#include <riscv/log_file.h>
|
||||
#include <fesvr/context.h>
|
||||
#include <fesvr/htif.h>
|
||||
#include <fesvr/memif.h>
|
||||
#include <fesvr/elfloader.h>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <vpi_user.h>
|
||||
#include <svdpi.h>
|
||||
#include "testchip_tsi.h"
|
||||
|
||||
extern testchip_tsi_t* tsi;
|
||||
#if __has_include("spiketile_tsi.h")
|
||||
#define SPIKETILE_HTIF_TSI
|
||||
extern htif_t* tsi;
|
||||
#endif
|
||||
#if __has_include("spiketile_dtm.h")
|
||||
#define SPIKETILE_HTIF_DTM
|
||||
extern htif_t* dtm;
|
||||
#endif
|
||||
|
||||
enum transfer_t {
|
||||
NToB,
|
||||
@@ -80,10 +89,11 @@ public:
|
||||
void tcm_a(uint64_t address, uint64_t data, uint32_t mask, uint32_t opcode, uint32_t size);
|
||||
bool tcm_d(uint64_t *data);
|
||||
|
||||
void loadmem(const char* fname);
|
||||
void loadmem(size_t base, const char* fname);
|
||||
|
||||
void drain_stq();
|
||||
bool stq_empty() { return st_q.size() == 0; };
|
||||
void flush_icache();
|
||||
|
||||
const cfg_t &get_cfg() const { return cfg; }
|
||||
const std::map<size_t, processor_t*>& get_harts() const { return harts; }
|
||||
@@ -109,6 +119,7 @@ public:
|
||||
bool fast_clint;
|
||||
cfg_t cfg;
|
||||
std::map<size_t, processor_t*> harts;
|
||||
bool accessed_tofrom_host;
|
||||
private:
|
||||
bool handle_cache_access(reg_t addr, size_t len,
|
||||
uint8_t* load_bytes,
|
||||
@@ -324,7 +335,7 @@ extern "C" void spike_tile(int hartid, char* isa,
|
||||
}
|
||||
}
|
||||
if (loadmem_file != "" && tcm_size > 0)
|
||||
simif->loadmem(loadmem_file.c_str());
|
||||
simif->loadmem(tcm_base, loadmem_file.c_str());
|
||||
|
||||
p->reset();
|
||||
p->get_state()->pc = reset_vector;
|
||||
@@ -334,14 +345,22 @@ extern "C" void spike_tile(int hartid, char* isa,
|
||||
tile_t* tile = tiles[hartid];
|
||||
chipyard_simif_t* simif = tile->simif;
|
||||
processor_t* proc = tile->proc;
|
||||
if (!simif->htif && tsi) {
|
||||
simif->htif = (htif_t*) tsi;
|
||||
}
|
||||
#if defined(SPIKETILE_HTIF_TSI)
|
||||
if (!simif->htif && tsi)
|
||||
simif->htif = tsi;
|
||||
#endif
|
||||
#if defined(SPIKETILE_HTIF_DTM)
|
||||
if (!simif->htif && dtm)
|
||||
simif->htif = dtm;
|
||||
#endif
|
||||
|
||||
simif->cycle = cycle;
|
||||
if (debug) {
|
||||
proc->halt_request = proc->HR_REGULAR;
|
||||
}
|
||||
if (!debug && proc->halt_request != proc->HR_NONE) {
|
||||
proc->halt_request = proc->HR_NONE;
|
||||
}
|
||||
|
||||
proc->get_state()->mip->backdoor_write_with_mask(MIP_MTIP, mtip ? MIP_MTIP : 0);
|
||||
proc->get_state()->mip->backdoor_write_with_mask(MIP_MSIP, msip ? MIP_MSIP : 0);
|
||||
@@ -350,6 +369,7 @@ extern "C" void spike_tile(int hartid, char* isa,
|
||||
|
||||
tile->max_insns = ipc;
|
||||
uint64_t pre_insns = proc->get_state()->minstret->read();
|
||||
simif->accessed_tofrom_host = false;
|
||||
tile->spike_context.switch_to();
|
||||
*insns_retired = proc->get_state()->minstret->read() - pre_insns;
|
||||
if (simif->use_stq) {
|
||||
@@ -439,6 +459,7 @@ chipyard_simif_t::chipyard_simif_t(size_t icache_ways,
|
||||
std::vector<size_t>(),
|
||||
false,
|
||||
0),
|
||||
accessed_tofrom_host(false),
|
||||
icache_ways(icache_ways),
|
||||
icache_sets(icache_sets),
|
||||
dcache_ways(dcache_ways),
|
||||
@@ -504,6 +525,12 @@ chipyard_simif_t::chipyard_simif_t(size_t icache_ways,
|
||||
tcm = (uint8_t*)malloc(tcm_size);
|
||||
}
|
||||
|
||||
void chipyard_simif_t::flush_icache() {
|
||||
for (auto &w : icache) {
|
||||
for (size_t i = 0; i < icache_sets; i++) w[i].state = NONE;
|
||||
}
|
||||
}
|
||||
|
||||
bool chipyard_simif_t::reservable(reg_t addr) {
|
||||
for (auto& r: cacheables) {
|
||||
if (addr >= r.base && addr < r.base + r.size) {
|
||||
@@ -544,6 +571,12 @@ bool chipyard_simif_t::mmio_load(reg_t addr, size_t len, uint8_t* bytes) {
|
||||
bool found = false;
|
||||
bool cacheable = false;
|
||||
bool readonly = false;
|
||||
reg_t tohost_addr = htif ? htif->get_tohost_addr() : 0;
|
||||
reg_t fromhost_addr = htif ? htif->get_fromhost_addr() : 0;
|
||||
if (addr == tohost_addr || addr == fromhost_addr) {
|
||||
accessed_tofrom_host = true;
|
||||
}
|
||||
|
||||
if (addr >= tcm_base && addr < tcm_base + tcm_size) {
|
||||
memcpy(bytes, tcm + addr - tcm_base, len);
|
||||
return true;
|
||||
@@ -579,6 +612,8 @@ bool chipyard_simif_t::mmio_load(reg_t addr, size_t len, uint8_t* bytes) {
|
||||
while (!handle_cache_access(addr, len, bytes, nullptr, LOAD)) {
|
||||
host->switch_to();
|
||||
}
|
||||
uint64_t lddata = 0;
|
||||
memcpy(&lddata, bytes, len);
|
||||
} else {
|
||||
handle_mmio_access(addr, len, bytes, nullptr, LOAD, readonly);
|
||||
}
|
||||
@@ -605,6 +640,7 @@ void chipyard_simif_t::handle_mmio_access(reg_t addr, size_t len,
|
||||
mmio_st = type == STORE;
|
||||
if (type == STORE) {
|
||||
assert(len <= 8);
|
||||
mmio_stdata = 0;
|
||||
memcpy(&mmio_stdata, store_bytes, len);
|
||||
}
|
||||
mmio_len = len;
|
||||
@@ -911,6 +947,13 @@ bool chipyard_simif_t::dcache_c(uint64_t* address, uint64_t* source, int* param,
|
||||
}
|
||||
|
||||
bool chipyard_simif_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes) {
|
||||
reg_t tohost_addr = htif ? htif->get_tohost_addr() : 0;
|
||||
reg_t fromhost_addr = htif ? htif->get_fromhost_addr() : 0;
|
||||
|
||||
if (addr == tohost_addr || addr == fromhost_addr) {
|
||||
accessed_tofrom_host = true;
|
||||
}
|
||||
|
||||
if (addr >= tcm_base && addr < tcm_base + tcm_size) {
|
||||
memcpy(tcm + addr - tcm_base, bytes, len);
|
||||
return true;
|
||||
@@ -936,6 +979,8 @@ bool chipyard_simif_t::mmio_store(reg_t addr, size_t len, const uint8_t* bytes)
|
||||
return false;
|
||||
}
|
||||
if (cacheable) {
|
||||
uint64_t temp = 0;
|
||||
memcpy(&temp, bytes, len);
|
||||
if (use_stq) {
|
||||
assert(len <= 8);
|
||||
uint64_t stdata;
|
||||
@@ -1009,30 +1054,28 @@ bool chipyard_simif_t::tcm_d(uint64_t* data) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#define parse_nibble(c) ((c) >= 'a' ? (c)-'a'+10 : (c)-'0')
|
||||
void chipyard_simif_t::loadmem(const char* fname) {
|
||||
std::ifstream in(fname);
|
||||
std::string line;
|
||||
if (!in.is_open()) {
|
||||
printf("SpikeTile couldn't open loadmem file %s\n", fname);
|
||||
abort();
|
||||
}
|
||||
size_t fsize = 0;
|
||||
size_t start = 0;
|
||||
while (std::getline(in, line)) {
|
||||
for (ssize_t i = line.length()-2, j = 0; i >= 0; i -= 2, j++) {
|
||||
char byte = (parse_nibble(line[i]) << 4) | parse_nibble(line[i+1]);
|
||||
ssize_t addr = (start + j) % tcm_size;
|
||||
tcm[addr] = (uint8_t)byte;
|
||||
void chipyard_simif_t::loadmem(size_t base, const char* fname) {
|
||||
class loadmem_memif_t : public memif_t {
|
||||
public:
|
||||
loadmem_memif_t(chipyard_simif_t* _simif, size_t _start) : memif_t(nullptr), simif(_simif), start(_start) {}
|
||||
void write(addr_t taddr, size_t len, const void* src) override
|
||||
{
|
||||
addr_t addr = taddr - start;
|
||||
memcpy(simif->tcm + addr, src, len);
|
||||
}
|
||||
start += line.length()/2;
|
||||
fsize += line.length()/2;
|
||||
void read(addr_t taddr, size_t len, void* bytes) override {
|
||||
assert(false);
|
||||
}
|
||||
endianness_t get_target_endianness() const override {
|
||||
return endianness_little;
|
||||
}
|
||||
private:
|
||||
chipyard_simif_t* simif;
|
||||
size_t start;
|
||||
} loadmem_memif(this, tcm_base);
|
||||
|
||||
if (fsize > tcm_size) {
|
||||
fprintf(stderr, "Loadmem file is too large\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
reg_t entry;
|
||||
load_elf(fname, &loadmem_memif, &entry);
|
||||
}
|
||||
|
||||
bool insn_should_fence(uint64_t bits) {
|
||||
@@ -1060,33 +1103,31 @@ void spike_thread_main(void* arg)
|
||||
// if (insn_should_fence(last_bits) && !simif->stq_empty()) {
|
||||
// host->switch_to();
|
||||
// }
|
||||
uint64_t old_minstret = state->minstret->read();
|
||||
proc->step(1);
|
||||
tile->max_insns--;
|
||||
if (proc->is_waiting_for_interrupt()) {
|
||||
if (simif->fast_clint) {
|
||||
// uint64_t mip = state->mip->read();
|
||||
// uint64_t mie = state->mie->read();
|
||||
//printf("Setting MTIP %x %x %x %x %lx\n", simif->cycle, old_minstret, mip, mie,
|
||||
// state->pc);
|
||||
state->mip->backdoor_write_with_mask(MIP_MTIP, MIP_MTIP);
|
||||
tile->max_insns = tile->max_insns <= 1 ? 0 : 1;
|
||||
} else {
|
||||
//printf("SpikeTile in WFI\n");
|
||||
tile->max_insns = 0;
|
||||
}
|
||||
}
|
||||
if (tile->max_insns % 100 == 0) {
|
||||
uint64_t old_minstret = state->minstret->read();
|
||||
uint64_t tohost_addr = simif->htif ? simif->htif->get_tohost_addr() : 0;
|
||||
uint64_t fromhost_addr = simif->htif ? simif->htif->get_fromhost_addr() : 0;
|
||||
auto& mem_read = state->log_mem_read;
|
||||
reg_t mem_read_addr = mem_read.empty() ? 0 : std::get<0>(mem_read[0]);
|
||||
if ((old_minstret == state->minstret->read()) ||
|
||||
(tohost_addr && mem_read_addr == tohost_addr) ||
|
||||
(fromhost_addr && mem_read_addr == fromhost_addr)) {
|
||||
tile->max_insns == 0;
|
||||
if (state->debug_mode) {
|
||||
// TODO: Fix. This needs to apply the same hack as rocket-chip...
|
||||
// JALRs in debug mode should flush the ICache.
|
||||
// There is no API to determine if a JALR was executed, so hack the
|
||||
// pc of the JALR in the debug rom here instead.
|
||||
if (state->pc == 0x838) {
|
||||
simif->flush_icache();
|
||||
}
|
||||
}
|
||||
|
||||
// If we get stuck in WFI, or we start polling tohost/fromhost, switch to host thread
|
||||
if ((old_minstret == state->minstret->read()) || simif->accessed_tofrom_host) {
|
||||
tile->max_insns = 0;
|
||||
}
|
||||
state->mcycle->write(simif->cycle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "DPI-C" function void cospike_set_sysinfo(
|
||||
input string isa,
|
||||
input string priv,
|
||||
input int pmpregions,
|
||||
input longint mem0_base,
|
||||
input longint mem0_size,
|
||||
@@ -16,12 +17,14 @@ import "DPI-C" function void cospike_cosim(input longint cycle,
|
||||
input bit raise_exception,
|
||||
input bit raise_interrupt,
|
||||
input longint cause,
|
||||
input longint wdata
|
||||
input longint wdata,
|
||||
input int priv
|
||||
);
|
||||
|
||||
|
||||
module SpikeCosim #(
|
||||
parameter ISA,
|
||||
parameter PRIV,
|
||||
parameter PMPREGIONS,
|
||||
parameter MEM0_BASE,
|
||||
parameter MEM0_SIZE,
|
||||
@@ -42,6 +45,7 @@ module SpikeCosim #(
|
||||
input [63:0] trace_0_cause,
|
||||
input trace_0_has_wdata,
|
||||
input [63:0] trace_0_wdata,
|
||||
input [2:0] trace_0_priv,
|
||||
|
||||
input trace_1_valid,
|
||||
input [63:0] trace_1_iaddr,
|
||||
@@ -50,11 +54,12 @@ module SpikeCosim #(
|
||||
input trace_1_interrupt,
|
||||
input [63:0] trace_1_cause,
|
||||
input trace_1_has_wdata,
|
||||
input [63:0] trace_1_wdata
|
||||
input [63:0] trace_1_wdata,
|
||||
input [2:0] trace_1_priv
|
||||
);
|
||||
|
||||
initial begin
|
||||
cospike_set_sysinfo(ISA, PMPREGIONS, MEM0_BASE, MEM0_SIZE, NHARTS, BOOTROM);
|
||||
cospike_set_sysinfo(ISA, PRIV, PMPREGIONS, MEM0_BASE, MEM0_SIZE, NHARTS, BOOTROM);
|
||||
end;
|
||||
|
||||
always @(posedge clock) begin
|
||||
@@ -62,12 +67,12 @@ module SpikeCosim #(
|
||||
if (trace_0_valid || trace_0_exception || trace_0_cause) begin
|
||||
cospike_cosim(cycle, hartid, trace_0_has_wdata, trace_0_valid, trace_0_iaddr,
|
||||
trace_0_insn, trace_0_exception, trace_0_interrupt, trace_0_cause,
|
||||
trace_0_wdata);
|
||||
trace_0_wdata, trace_0_priv);
|
||||
end
|
||||
if (trace_1_valid || trace_1_exception || trace_1_cause) begin
|
||||
cospike_cosim(cycle, hartid, trace_1_has_wdata, trace_1_valid, trace_1_iaddr,
|
||||
trace_1_insn, trace_1_exception, trace_1_interrupt, trace_1_cause,
|
||||
trace_1_wdata);
|
||||
trace_1_wdata, trace_1_priv);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,15 +14,18 @@ import testchipip.TileTraceIO
|
||||
|
||||
case class SpikeCosimConfig(
|
||||
isa: String,
|
||||
priv: String,
|
||||
pmpregions: Int,
|
||||
mem0_base: BigInt,
|
||||
mem0_size: BigInt,
|
||||
nharts: Int,
|
||||
bootrom: String
|
||||
bootrom: String,
|
||||
has_dtm: Boolean
|
||||
)
|
||||
|
||||
class SpikeCosim(cfg: SpikeCosimConfig) extends BlackBox(Map(
|
||||
"ISA" -> StringParam(cfg.isa),
|
||||
"PRIV" -> StringParam(cfg.priv),
|
||||
"PMPREGIONS" -> IntParam(cfg.pmpregions),
|
||||
"MEM0_BASE" -> IntParam(cfg.mem0_base),
|
||||
"MEM0_SIZE" -> IntParam(cfg.mem0_size),
|
||||
@@ -32,6 +35,7 @@ class SpikeCosim(cfg: SpikeCosimConfig) extends BlackBox(Map(
|
||||
{
|
||||
addResource("/csrc/cospike.cc")
|
||||
addResource("/vsrc/cospike.v")
|
||||
if (cfg.has_dtm) addResource("/csrc/cospike_dtm.h")
|
||||
val io = IO(new Bundle {
|
||||
val clock = Input(Clock())
|
||||
val reset = Input(Bool())
|
||||
@@ -46,6 +50,7 @@ class SpikeCosim(cfg: SpikeCosimConfig) extends BlackBox(Map(
|
||||
val cause = UInt(64.W)
|
||||
val has_wdata = Bool()
|
||||
val wdata = UInt(64.W)
|
||||
val priv = UInt(3.W)
|
||||
}))
|
||||
})
|
||||
}
|
||||
@@ -64,25 +69,23 @@ object SpikeCosim
|
||||
require(trace.numInsns <= 2)
|
||||
cosim.io.cycle := cycle
|
||||
cosim.io.trace.map(t => {
|
||||
t := DontCare
|
||||
t.valid := false.B
|
||||
t.iaddr := 0.U
|
||||
t.insn := 0.U
|
||||
t.exception := false.B
|
||||
t.interrupt := false.B
|
||||
t.cause := 0.U
|
||||
})
|
||||
cosim.io.hartid := hartid.U
|
||||
for (i <- 0 until trace.numInsns) {
|
||||
cosim.io.trace(i).valid := trace.insns(i).valid
|
||||
val insn = trace.trace.insns(i)
|
||||
cosim.io.trace(i).valid := insn.valid
|
||||
val signed = Wire(SInt(64.W))
|
||||
signed := trace.insns(i).iaddr.asSInt
|
||||
signed := insn.iaddr.asSInt
|
||||
cosim.io.trace(i).iaddr := signed.asUInt
|
||||
cosim.io.trace(i).insn := trace.insns(i).insn
|
||||
cosim.io.trace(i).exception := trace.insns(i).exception
|
||||
cosim.io.trace(i).interrupt := trace.insns(i).interrupt
|
||||
cosim.io.trace(i).cause := trace.insns(i).cause
|
||||
cosim.io.trace(i).has_wdata := trace.insns(i).wdata.isDefined.B
|
||||
cosim.io.trace(i).wdata := trace.insns(i).wdata.getOrElse(0.U)
|
||||
cosim.io.trace(i).insn := insn.insn
|
||||
cosim.io.trace(i).exception := insn.exception
|
||||
cosim.io.trace(i).interrupt := insn.interrupt
|
||||
cosim.io.trace(i).cause := insn.cause
|
||||
cosim.io.trace(i).has_wdata := insn.wdata.isDefined.B
|
||||
cosim.io.trace(i).wdata := insn.wdata.getOrElse(0.U)
|
||||
cosim.io.trace(i).priv := insn.priv
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ import freechips.rocketchip.devices.tilelink._
|
||||
|
||||
// DOC include start: DigitalTop
|
||||
class DigitalTop(implicit p: Parameters) extends ChipyardSystem
|
||||
with testchipip.CanHavePeripheryUARTTSI // Enables optional UART-based TSI transport
|
||||
with testchipip.CanHavePeripheryCustomBootPin // Enables optional custom boot pin
|
||||
with testchipip.HasPeripheryBootAddrReg // Use programmable boot address register
|
||||
with testchipip.CanHavePeripheryBootAddrReg // Use programmable boot address register
|
||||
with testchipip.CanHaveTraceIO // Enables optionally adding trace IO
|
||||
with testchipip.CanHaveBackingScratchpad // Enables optionally adding a backing scratchpad
|
||||
with testchipip.CanHaveBankedScratchpad // Enables optionally adding a banked scratchpad
|
||||
with testchipip.CanHavePeripheryBlockDevice // Enables optionally adding the block device
|
||||
with testchipip.CanHavePeripheryTLSerial // Enables optionally adding the backing memory and serial adapter
|
||||
with sifive.blocks.devices.i2c.HasPeripheryI2C // Enables optionally adding the sifive I2C
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
package chipyard
|
||||
|
||||
import chisel3._
|
||||
|
||||
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
import org.chipsalliance.cde.config.{Field, Parameters, Config}
|
||||
import freechips.rocketchip.util.{ResetCatchAndSync}
|
||||
import freechips.rocketchip.prci._
|
||||
|
||||
import chipyard.harness.{ApplyHarnessBinders, HarnessBinders}
|
||||
import chipyard.iobinders.HasIOBinders
|
||||
import chipyard.clocking.{SimplePllConfiguration, ClockDividerN}
|
||||
import chipyard.HarnessClockInstantiatorKey
|
||||
|
||||
|
||||
// HarnessClockInstantiators are classes which generate clocks that drive
|
||||
// TestHarness simulation models and any Clock inputs to the ChipTop
|
||||
trait HarnessClockInstantiator {
|
||||
val _clockMap: LinkedHashMap[String, (Double, ClockBundle)] = LinkedHashMap.empty
|
||||
|
||||
// request a clock bundle at a particular frequency
|
||||
def requestClockBundle(name: String, freqRequested: Double): ClockBundle = {
|
||||
val clockBundle = Wire(new ClockBundle(ClockBundleParameters()))
|
||||
_clockMap(name) = (freqRequested, clockBundle)
|
||||
clockBundle
|
||||
}
|
||||
|
||||
// refClock is the clock generated by TestDriver that is
|
||||
// passed to the TestHarness as its implicit clock
|
||||
def instantiateHarnessClocks(refClock: ClockBundle): Unit
|
||||
}
|
||||
|
||||
// The DividerOnlyHarnessClockInstantiator uses synthesizable clock divisors
|
||||
// to approximate frequency ratios between the requested clocks
|
||||
class DividerOnlyHarnessClockInstantiator extends HarnessClockInstantiator {
|
||||
// connect all clock wires specified to a divider only PLL
|
||||
def instantiateHarnessClocks(refClock: ClockBundle): Unit = {
|
||||
val sinks = _clockMap.map({ case (name, (freq, bundle)) =>
|
||||
ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))), name=Some(name))
|
||||
}).toSeq
|
||||
|
||||
val pllConfig = new SimplePllConfiguration("harnessDividerOnlyClockGenerator", sinks)
|
||||
pllConfig.emitSummaries()
|
||||
|
||||
val dividedClocks = LinkedHashMap[Int, Clock]()
|
||||
def instantiateDivider(div: Int): Clock = {
|
||||
val divider = Module(new ClockDividerN(div))
|
||||
divider.suggestName(s"ClockDivideBy${div}")
|
||||
divider.io.clk_in := refClock.clock
|
||||
dividedClocks(div) = divider.io.clk_out
|
||||
divider.io.clk_out
|
||||
}
|
||||
|
||||
// connect wires to clock source
|
||||
for (sinkParams <- sinks) {
|
||||
// bypass the reference freq. (don't create a divider + reset sync)
|
||||
val (divClock, divReset) = if (sinkParams.take.get.freqMHz != pllConfig.referenceFreqMHz) {
|
||||
val div = pllConfig.sinkDividerMap(sinkParams)
|
||||
val divClock = dividedClocks.getOrElse(div, instantiateDivider(div))
|
||||
(divClock, ResetCatchAndSync(divClock, refClock.reset.asBool))
|
||||
} else {
|
||||
(refClock.clock, refClock.reset)
|
||||
}
|
||||
|
||||
_clockMap(sinkParams.name.get)._2.clock := divClock
|
||||
_clockMap(sinkParams.name.get)._2.reset := divReset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The AbsoluteFreqHarnessClockInstantiator uses a Verilog blackbox to
|
||||
// provide the precise requested frequency.
|
||||
// This ClockInstantiator cannot be synthesized, run in Verilator, or run in FireSim
|
||||
// It is useful for VCS/Xcelium-driven RTL simulations
|
||||
class AbsoluteFreqHarnessClockInstantiator extends HarnessClockInstantiator {
|
||||
def instantiateHarnessClocks(refClock: ClockBundle): Unit = {
|
||||
val sinks = _clockMap.map({ case (name, (freq, bundle)) =>
|
||||
ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))), name=Some(name))
|
||||
}).toSeq
|
||||
|
||||
// connect wires to clock source
|
||||
for (sinkParams <- sinks) {
|
||||
val source = Module(new ClockSourceAtFreq(sinkParams.take.get.freqMHz))
|
||||
source.io.power := true.B
|
||||
source.io.gate := false.B
|
||||
|
||||
_clockMap(sinkParams.name.get)._2.clock := source.io.clk
|
||||
_clockMap(sinkParams.name.get)._2.reset := refClock.reset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WithAbsoluteFreqHarnessClockInstantiator extends Config((site, here, up) => {
|
||||
case HarnessClockInstantiatorKey => () => new AbsoluteFreqHarnessClockInstantiator
|
||||
})
|
||||
@@ -25,7 +25,6 @@ import barstools.iocell.chisel._
|
||||
import testchipip._
|
||||
import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvonly}
|
||||
import chipyard.{CanHaveMasterTLMemPort}
|
||||
import chipyard.clocking.{HasChipyardPRCI, DividerOnlyClockGenerator}
|
||||
|
||||
import scala.reflect.{ClassTag}
|
||||
|
||||
@@ -304,6 +303,15 @@ class WithSerialTLIOCells extends OverrideIOBinder({
|
||||
}).getOrElse((Nil, Nil))
|
||||
})
|
||||
|
||||
class WithSerialTLPunchthrough extends OverrideIOBinder({
|
||||
(system: CanHavePeripheryTLSerial) => system.serial_tl.map({ s =>
|
||||
val sys = system.asInstanceOf[BaseSubsystem]
|
||||
val port = IO(s.getWrappedValue.cloneType)
|
||||
port <> s.getWrappedValue
|
||||
(Seq(port), Nil)
|
||||
}).getOrElse((Nil, Nil))
|
||||
})
|
||||
|
||||
class WithAXI4MemPunchthrough extends OverrideLazyIOBinder({
|
||||
(system: CanHaveMasterAXI4MemPort) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
@@ -412,6 +420,15 @@ class WithCustomBootPin extends OverrideIOBinder({
|
||||
}).getOrElse((Nil, Nil))
|
||||
})
|
||||
|
||||
class WithUARTTSIPunchthrough extends OverrideIOBinder({
|
||||
(system: CanHavePeripheryUARTTSI) => system.uart_tsi.map({ p =>
|
||||
val sys = system.asInstanceOf[BaseSubsystem]
|
||||
val uart_tsi = IO(new UARTTSIIO(p.uartParams))
|
||||
uart_tsi <> p
|
||||
(Seq(uart_tsi), Nil)
|
||||
}).getOrElse((Nil, Nil))
|
||||
})
|
||||
|
||||
class WithTLMemPunchthrough extends OverrideIOBinder({
|
||||
(system: CanHaveMasterTLMemPort) => {
|
||||
val io_tl_mem_pins_temp = IO(DataMirror.internal.chiselTypeClone[HeterogeneousBag[TLBundle]](system.mem_tl)).suggestName("tl_slave")
|
||||
|
||||
@@ -7,6 +7,7 @@ import chisel3.experimental.{IntParam, StringParam, IO}
|
||||
import org.chipsalliance.cde.config._
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.devices.tilelink._
|
||||
import freechips.rocketchip.devices.debug.{ExportDebug, DMI}
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.rocket._
|
||||
import freechips.rocketchip.tilelink._
|
||||
@@ -61,6 +62,7 @@ case class SpikeCoreParams() extends CoreParams {
|
||||
val useBitManipCrypto = false
|
||||
val useCryptoNIST = false
|
||||
val useCryptoSM = false
|
||||
val useConditionalZero = false
|
||||
|
||||
override def vLen = 128
|
||||
override def vMemDataBits = 128
|
||||
@@ -189,7 +191,8 @@ class SpikeBlackBox(
|
||||
readonly_uncacheable_regions: String,
|
||||
executable_regions: String,
|
||||
tcm_base: BigInt,
|
||||
tcm_size: BigInt) extends BlackBox(Map(
|
||||
tcm_size: BigInt,
|
||||
use_dtm: Boolean) extends BlackBox(Map(
|
||||
"HARTID" -> IntParam(hartId),
|
||||
"ISA" -> StringParam(isa),
|
||||
"PMPREGIONS" -> IntParam(pmpregions),
|
||||
@@ -302,7 +305,11 @@ class SpikeBlackBox(
|
||||
})
|
||||
addResource("/vsrc/spiketile.v")
|
||||
addResource("/csrc/spiketile.cc")
|
||||
|
||||
if (use_dtm) {
|
||||
addResource("/csrc/spiketile_dtm.h")
|
||||
} else {
|
||||
addResource("/csrc/spiketile_tsi.h")
|
||||
}
|
||||
}
|
||||
|
||||
class SpikeTileModuleImp(outer: SpikeTile) extends BaseTileModuleImp(outer) {
|
||||
@@ -326,13 +333,18 @@ class SpikeTileModuleImp(outer: SpikeTile) extends BaseTileModuleImp(outer) {
|
||||
val (dcache_tl, dcacheEdge) = outer.dcacheNode.out(0)
|
||||
val (mmio_tl, mmioEdge) = outer.mmioNode.out(0)
|
||||
|
||||
// Note: This assumes that if the debug module exposes the ClockedDMI port,
|
||||
// then the DTM-based bringup with SimDTM will be used. This isn't required to be
|
||||
// true, but it usually is
|
||||
val useDTM = p(ExportDebug).protocols.contains(DMI)
|
||||
val spike = Module(new SpikeBlackBox(hartId, isaDTS, tileParams.core.nPMPs,
|
||||
tileParams.icache.get.nSets, tileParams.icache.get.nWays,
|
||||
tileParams.dcache.get.nSets, tileParams.dcache.get.nWays,
|
||||
tileParams.dcache.get.nMSHRs,
|
||||
cacheable_regions, uncacheable_regions, readonly_uncacheable_regions, executable_regions,
|
||||
outer.spikeTileParams.tcmParams.map(_.base).getOrElse(0),
|
||||
outer.spikeTileParams.tcmParams.map(_.size).getOrElse(0)
|
||||
outer.spikeTileParams.tcmParams.map(_.size).getOrElse(0),
|
||||
useDTM
|
||||
))
|
||||
spike.io.clock := clock.asBool
|
||||
val cycle = RegInit(0.U(64.W))
|
||||
@@ -421,7 +433,7 @@ class SpikeTileModuleImp(outer: SpikeTile) extends BaseTileModuleImp(outer) {
|
||||
|
||||
spike.io.mmio.a.ready := mmio_tl.a.ready
|
||||
mmio_tl.a.valid := spike.io.mmio.a.valid
|
||||
val log_size = MuxCase(0.U, (0 until 3).map { i => (spike.io.mmio.a.size === (1 << i).U) -> i.U })
|
||||
val log_size = (0 until 4).map { i => Mux(spike.io.mmio.a.size === (1 << i).U, i.U, 0.U) }.reduce(_|_)
|
||||
mmio_tl.a.bits := Mux(spike.io.mmio.a.store,
|
||||
mmioEdge.Put(0.U, spike.io.mmio.a.address, log_size, spike.io.mmio.a.data)._2,
|
||||
mmioEdge.Get(0.U, spike.io.mmio.a.address, log_size)._2)
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package chipyard
|
||||
|
||||
import chisel3._
|
||||
|
||||
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
import org.chipsalliance.cde.config.{Field, Parameters}
|
||||
import freechips.rocketchip.util.{ResetCatchAndSync}
|
||||
import freechips.rocketchip.prci.{ClockBundle, ClockBundleParameters, ClockSinkParameters, ClockParameters}
|
||||
|
||||
import chipyard.harness.{ApplyHarnessBinders, HarnessBinders}
|
||||
import chipyard.iobinders.HasIOBinders
|
||||
import chipyard.clocking.{SimplePllConfiguration, ClockDividerN}
|
||||
|
||||
// -------------------------------
|
||||
// Chipyard Test Harness
|
||||
// -------------------------------
|
||||
|
||||
case object BuildTop extends Field[Parameters => LazyModule]((p: Parameters) => new ChipTop()(p))
|
||||
case object DefaultClockFrequencyKey extends Field[Double](100.0) // MHz
|
||||
case object HarnessClockInstantiatorKey extends Field[() => HarnessClockInstantiator](() => new DividerOnlyHarnessClockInstantiator)
|
||||
|
||||
trait HasHarnessSignalReferences {
|
||||
implicit val p: Parameters
|
||||
val harnessClockInstantiator = p(HarnessClockInstantiatorKey)()
|
||||
// clock/reset of the chiptop reference clock (can be different than the implicit harness clock/reset)
|
||||
var refClockFreq: Double = p(DefaultClockFrequencyKey)
|
||||
def setRefClockFreq(freqMHz: Double) = { refClockFreq = freqMHz }
|
||||
def getRefClockFreq: Double = refClockFreq
|
||||
def buildtopClock: Clock
|
||||
def buildtopReset: Reset
|
||||
def success: Bool
|
||||
}
|
||||
|
||||
class TestHarness(implicit val p: Parameters) extends Module with HasHarnessSignalReferences {
|
||||
val io = IO(new Bundle {
|
||||
val success = Output(Bool())
|
||||
})
|
||||
|
||||
val buildtopClock = Wire(Clock())
|
||||
val buildtopReset = Wire(Reset())
|
||||
|
||||
val lazyDut = LazyModule(p(BuildTop)(p)).suggestName("chiptop")
|
||||
val dut = Module(lazyDut.module)
|
||||
|
||||
io.success := false.B
|
||||
|
||||
val success = io.success
|
||||
|
||||
lazyDut match { case d: HasIOBinders =>
|
||||
ApplyHarnessBinders(this, d.lazySystem, d.portMap)
|
||||
}
|
||||
|
||||
val refClkBundle = harnessClockInstantiator.requestClockBundle("buildtop_reference_clock", getRefClockFreq * (1000 * 1000))
|
||||
|
||||
buildtopClock := refClkBundle.clock
|
||||
buildtopReset := WireInit(refClkBundle.reset)
|
||||
|
||||
val implicitHarnessClockBundle = Wire(new ClockBundle(ClockBundleParameters()))
|
||||
implicitHarnessClockBundle.clock := clock
|
||||
implicitHarnessClockBundle.reset := reset
|
||||
harnessClockInstantiator.instantiateHarnessClocks(implicitHarnessClockBundle)
|
||||
}
|
||||
@@ -13,53 +13,11 @@ class ClockWithFreq(val freqMHz: Double) extends Bundle {
|
||||
val clock = Clock()
|
||||
}
|
||||
|
||||
// This uses synthesizable clock divisors to approximate frequency rations
|
||||
// between the requested clocks. This is currently the defualt clock generator "model",
|
||||
// as it can be used in VCS/Xcelium/Verilator/FireSim
|
||||
class WithDividerOnlyClockGenerator extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
// Connect the implicit clock
|
||||
implicit val p = GetSystemParameters(system)
|
||||
val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock"))))
|
||||
system.connectImplicitClockSinkNode(implicitClockSinkNode)
|
||||
InModuleBody {
|
||||
val implicit_clock = implicitClockSinkNode.in.head._1.clock
|
||||
val implicit_reset = implicitClockSinkNode.in.head._1.reset
|
||||
system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => {
|
||||
l.clock := implicit_clock
|
||||
l.reset := implicit_reset
|
||||
}}
|
||||
}
|
||||
|
||||
// Connect all other requested clocks
|
||||
val referenceClockSource = ClockSourceNode(Seq(ClockSourceParameters()))
|
||||
val dividerOnlyClockGen = LazyModule(new DividerOnlyClockGenerator("buildTopClockGenerator"))
|
||||
|
||||
(system.allClockGroupsNode
|
||||
:= dividerOnlyClockGen.node
|
||||
:= referenceClockSource)
|
||||
|
||||
InModuleBody {
|
||||
val clock_wire = Wire(Input(new ClockWithFreq(dividerOnlyClockGen.module.referenceFreq)))
|
||||
val reset_wire = Wire(Input(AsyncReset()))
|
||||
val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey))
|
||||
val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey))
|
||||
|
||||
referenceClockSource.out.unzip._1.map { o =>
|
||||
o.clock := clock_wire.clock
|
||||
o.reset := reset_wire
|
||||
}
|
||||
|
||||
(Seq(clock_io, reset_io), clockIOCell ++ resetIOCell)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// This uses the FakePLL, which uses a ClockAtFreq Verilog blackbox to generate
|
||||
// the requested clocks. This also adds TileLink ClockDivider and ClockSelector
|
||||
// blocks, which allow memory-mapped control of clock division, and clock muxing
|
||||
// between the FakePLL and the slow off-chip clock
|
||||
// Note: This will not simulate properly with verilator or firesim
|
||||
// Note: This will not simulate properly with firesim
|
||||
class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
// Connect the implicit clock
|
||||
@@ -80,9 +38,9 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
val clockSelector = system.prci_ctrl_domain { LazyModule(new TLClockSelector(baseAddress + 0x30000, tlbus.beatBytes)) }
|
||||
val pllCtrl = system.prci_ctrl_domain { LazyModule(new FakePLLCtrl (baseAddress + 0x40000, tlbus.beatBytes)) }
|
||||
|
||||
tlbus.toVariableWidthSlave(Some("clock-div-ctrl")) { clockDivider.tlNode := TLBuffer() }
|
||||
tlbus.toVariableWidthSlave(Some("clock-sel-ctrl")) { clockSelector.tlNode := TLBuffer() }
|
||||
tlbus.toVariableWidthSlave(Some("pll-ctrl")) { pllCtrl.tlNode := TLBuffer() }
|
||||
clockDivider.tlNode := system.prci_ctrl_domain { TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := system.prci_ctrl_bus.get }
|
||||
clockSelector.tlNode := system.prci_ctrl_domain { TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := system.prci_ctrl_bus.get }
|
||||
pllCtrl.tlNode := system.prci_ctrl_domain { TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := system.prci_ctrl_bus.get }
|
||||
|
||||
system.allClockGroupsNode := clockDivider.clockNode := clockSelector.clockNode
|
||||
|
||||
@@ -100,7 +58,7 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
pllCtrlSink := pllCtrl.ctrlNode
|
||||
|
||||
InModuleBody {
|
||||
val clock_wire = Wire(Input(new ClockWithFreq(80)))
|
||||
val clock_wire = Wire(Input(new ClockWithFreq(100)))
|
||||
val reset_wire = Wire(Input(AsyncReset()))
|
||||
val (clock_io, clockIOCell) = IOCell.generateIOFromSignal(clock_wire, "clock", p(IOCellKey))
|
||||
val (reset_io, resetIOCell) = IOCell.generateIOFromSignal(reset_wire, "reset", p(IOCellKey))
|
||||
@@ -125,3 +83,43 @@ class WithPLLSelectorDividerClockGenerator extends OverrideLazyIOBinder({
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// This passes all clocks through to the TestHarness
|
||||
class WithPassthroughClockGenerator extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
// Connect the implicit clock
|
||||
implicit val p = GetSystemParameters(system)
|
||||
val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock"))))
|
||||
system.connectImplicitClockSinkNode(implicitClockSinkNode)
|
||||
InModuleBody {
|
||||
val implicit_clock = implicitClockSinkNode.in.head._1.clock
|
||||
val implicit_reset = implicitClockSinkNode.in.head._1.reset
|
||||
system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => {
|
||||
l.clock := implicit_clock
|
||||
l.reset := implicit_reset
|
||||
}}
|
||||
}
|
||||
|
||||
// This aggregate node should do nothing
|
||||
val clockGroupAggNode = ClockGroupAggregateNode("fake")
|
||||
val clockGroupsSourceNode = ClockGroupSourceNode(Seq(ClockGroupSourceParameters()))
|
||||
system.allClockGroupsNode := clockGroupAggNode := clockGroupsSourceNode
|
||||
|
||||
InModuleBody {
|
||||
val reset_io = IO(Input(AsyncReset()))
|
||||
require(clockGroupAggNode.out.size == 1)
|
||||
val (bundle, edge) = clockGroupAggNode.out(0)
|
||||
|
||||
val clock_ios = (bundle.member.data zip edge.sink.members).map { case (b, m) =>
|
||||
require(m.take.isDefined, s"""Clock ${m.name.get} has no requested frequency
|
||||
|Clocks: ${edge.sink.members.map(_.name.get)}""".stripMargin)
|
||||
val freq = m.take.get.freqMHz
|
||||
val clock_io = IO(Input(new ClockWithFreq(freq))).suggestName(s"clock_${m.name.get}")
|
||||
b.clock := clock_io.clock
|
||||
b.reset := reset_io
|
||||
clock_io
|
||||
}.toSeq
|
||||
((clock_ios :+ reset_io), Nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,10 +23,9 @@ object ClockGroupCombiner {
|
||||
case object ClockGroupCombinerKey extends Field[Seq[(String, ClockSinkParameters => Boolean)]](Nil)
|
||||
|
||||
// All clock groups with a name containing any substring in names will be combined into a single clock group
|
||||
class WithClockGroupsCombinedByName(grouped_name: String, names: String*) extends Config((site, here, up) => {
|
||||
case ClockGroupCombinerKey => {
|
||||
val combiner: ClockSinkParameters => Boolean = { m => names.map(n => m.name.get.contains(n)).reduce(_||_) }
|
||||
up(ClockGroupCombinerKey) ++ Seq((grouped_name, combiner))
|
||||
class WithClockGroupsCombinedByName(groups: (String, Seq[String], Seq[String])*) extends Config((site, here, up) => {
|
||||
case ClockGroupCombinerKey => groups.map { case (grouped_name, matched_names, unmatched_names) =>
|
||||
(grouped_name, (m: ClockSinkParameters) => matched_names.exists(n => m.name.get.contains(n)) && !unmatched_names.exists(n => m.name.get.contains(n)))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -49,9 +48,14 @@ class ClockGroupCombiner(implicit p: Parameters, v: ValName) extends LazyModule
|
||||
val name = combiners(i)._1
|
||||
i = i + 1
|
||||
require(g.size >= 1)
|
||||
require(g.forall(_.take.get == g.head.take.get))
|
||||
(grouped ++ Seq(ClockSinkParameters(take = g.head.take, name = Some(name))), r)
|
||||
val takes = g.map(_.take).flatten
|
||||
require(takes.distinct.size <= 1,
|
||||
s"Clock group $name has non-homogeneous requested ClockParameters $takes")
|
||||
require(takes.size > 0,
|
||||
s"Clock group $name has no inheritable frequencies")
|
||||
(grouped ++ Seq(ClockSinkParameters(take = takes.headOption, name = Some(name))), r)
|
||||
}
|
||||
|
||||
ClockGroupSinkParameters(
|
||||
name = u.name,
|
||||
members = grouped ++ rest
|
||||
|
||||
@@ -60,23 +60,23 @@ object ClockGroupNamePrefixer {
|
||||
* The default if all functions return None.
|
||||
*/
|
||||
object ClockGroupFrequencySpecifier {
|
||||
def apply(
|
||||
assigners: Seq[(String) => Option[Double]],
|
||||
defaultFreq: Double)(
|
||||
implicit p: Parameters, valName: ValName): ClockGroupAdapterNode = {
|
||||
def apply(assigners: Seq[(String) => Option[Double]])(
|
||||
implicit p: Parameters, valName: ValName): ClockGroupAdapterNode = {
|
||||
|
||||
def lookupFrequencyForName(clock: ClockSinkParameters): ClockSinkParameters = {
|
||||
require(clock.name.nonEmpty, "All clocks in clock group must have an assigned name")
|
||||
val clockFreq = assigners.foldLeft(defaultFreq)(
|
||||
(currentFreq, candidateFunc) => candidateFunc(clock.name.get).getOrElse(currentFreq))
|
||||
|
||||
clock.copy(take = clock.take match {
|
||||
case Some(cp) =>
|
||||
println(s"Clock ${clock.name.get}: using diplomatically specified frequency of ${cp.freqMHz}.")
|
||||
Some(cp)
|
||||
case None => Some(ClockParameters(clockFreq))
|
||||
})
|
||||
}
|
||||
def lookupFrequencyForName(clock: ClockSinkParameters): ClockSinkParameters = clock.copy(take = clock.take match {
|
||||
case Some(cp) =>
|
||||
println(s"Clock ${clock.name.get}: using diplomatically specified frequency of ${cp.freqMHz}.")
|
||||
Some(cp)
|
||||
case None => {
|
||||
val freqs = assigners.map { f => f(clock.name.get) }.flatten
|
||||
if (freqs.size > 0) {
|
||||
println(s"Clock ${clock.name.get}: using specified frequency of ${freqs.last}")
|
||||
Some(ClockParameters(freqs.last))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
LazyModule(new ClockGroupParameterModifier(sinkFn = { s => s.copy(members = s.members.map(lookupFrequencyForName)) })).node
|
||||
}
|
||||
|
||||
@@ -92,57 +92,3 @@ class SimplePllConfiguration(
|
||||
def referenceSinkParams(): ClockSinkParameters = sinkDividerMap.find(_._2 == 1).get._1
|
||||
}
|
||||
|
||||
case class DividerOnlyClockGeneratorNode(pllName: String)(implicit valName: ValName)
|
||||
extends MixedNexusNode(ClockImp, ClockGroupImp)(
|
||||
dFn = { _ => ClockGroupSourceParameters() },
|
||||
uFn = { u =>
|
||||
require(u.size == 1)
|
||||
require(!u.head.members.contains(None),
|
||||
"All output clocks in group must set their take parameters. Use a ClockGroupDealiaser")
|
||||
ClockSinkParameters(
|
||||
name = Some(s"${pllName}_reference_input"),
|
||||
take = Some(ClockParameters(new SimplePllConfiguration(pllName, u.head.members).referenceFreqMHz))) }
|
||||
)
|
||||
|
||||
/**
|
||||
* Generates a digital-divider-only PLL model that verilator can simulate.
|
||||
* Inspects all take-specified frequencies in the output ClockGroup, calculates a
|
||||
* fast reference clock (roughly LCM(requested frequencies)) which is passed up the
|
||||
* diplomatic graph, and then generates dividers for each unique requested
|
||||
* frequency.
|
||||
*
|
||||
* Output resets are not synchronized to generated clocks and should be
|
||||
* synchronized by the user in a manner they see fit.
|
||||
*
|
||||
*/
|
||||
|
||||
class DividerOnlyClockGenerator(pllName: String)(implicit p: Parameters, valName: ValName) extends LazyModule {
|
||||
val node = DividerOnlyClockGeneratorNode(pllName)
|
||||
|
||||
lazy val module = new Impl
|
||||
class Impl extends LazyRawModuleImp(this) {
|
||||
require(node.out.size == 1, "Idealized PLL expects to generate a single output clock group. Use a ClockGroupAggregator")
|
||||
val (refClock, ClockEdgeParameters(_, refSinkParam, _, _)) = node.in.head
|
||||
val (outClocks, ClockGroupEdgeParameters(_, outSinkParams, _, _)) = node.out.head
|
||||
|
||||
val referenceFreq = refSinkParam.take.get.freqMHz
|
||||
val pllConfig = new SimplePllConfiguration(pllName, outSinkParams.members)
|
||||
pllConfig.emitSummaries()
|
||||
|
||||
val dividedClocks = mutable.HashMap[Int, Clock]()
|
||||
def instantiateDivider(div: Int): Clock = {
|
||||
val divider = Module(new ClockDividerN(div))
|
||||
divider.suggestName(s"ClockDivideBy${div}")
|
||||
divider.io.clk_in := refClock.clock
|
||||
dividedClocks(div) = divider.io.clk_out
|
||||
divider.io.clk_out
|
||||
}
|
||||
|
||||
for (((sinkBName, sinkB), sinkP) <- outClocks.member.elements.zip(outSinkParams.members)) {
|
||||
val div = pllConfig.sinkDividerMap(sinkP)
|
||||
sinkB.clock := dividedClocks.getOrElse(div, instantiateDivider(div))
|
||||
// Reset handling and synchronization is expected to be handled by a downstream node
|
||||
sinkB.reset := refClock.reset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,15 +14,17 @@ import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.tile._
|
||||
import freechips.rocketchip.prci._
|
||||
|
||||
import testchipip.{TLTileResetCtrl}
|
||||
import chipyard.{DefaultClockFrequencyKey}
|
||||
import testchipip.{TLTileResetCtrl, ClockGroupFakeResetSynchronizer}
|
||||
|
||||
case class ChipyardPRCIControlParams(
|
||||
slaveWhere: TLBusWrapperLocation = CBUS,
|
||||
baseAddress: BigInt = 0x100000,
|
||||
enableTileClockGating: Boolean = true,
|
||||
enableTileResetSetting: Boolean = true
|
||||
)
|
||||
enableTileResetSetting: Boolean = true,
|
||||
enableResetSynchronizers: Boolean = true // this should only be disabled to work around verilator async-reset initialization problems
|
||||
) {
|
||||
def generatePRCIXBar = enableTileClockGating || enableTileResetSetting
|
||||
}
|
||||
|
||||
|
||||
case object ChipyardPRCIControlKey extends Field[ChipyardPRCIControlParams](ChipyardPRCIControlParams())
|
||||
@@ -37,6 +39,13 @@ trait HasChipyardPRCI { this: BaseSubsystem with InstantiatesTiles =>
|
||||
val prci_ctrl_domain = LazyModule(new ClockSinkDomain(name=Some("chipyard-prci-control")))
|
||||
prci_ctrl_domain.clockNode := tlbus.fixedClockNode
|
||||
|
||||
val prci_ctrl_bus = Option.when(prciParams.generatePRCIXBar) { prci_ctrl_domain { TLXbar() } }
|
||||
prci_ctrl_bus.foreach(xbar => tlbus.coupleTo("prci_ctrl") { (xbar
|
||||
:= TLFIFOFixer(TLFIFOFixer.all)
|
||||
:= TLBuffer()
|
||||
:= _)
|
||||
})
|
||||
|
||||
// Aggregate all the clock groups into a single node
|
||||
val aggregator = LazyModule(new ClockGroupAggregator("allClocks")).node
|
||||
val allClockGroupsNode = ClockGroupEphemeralNode()
|
||||
@@ -70,21 +79,47 @@ trait HasChipyardPRCI { this: BaseSubsystem with InstantiatesTiles =>
|
||||
// 5. Add reset control registers to the tiles (if desired)
|
||||
// The final clock group here contains physically distinct clock domains, which some PRCI node in a
|
||||
// diplomatic IOBinder should drive
|
||||
val frequencySpecifier = ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey), p(DefaultClockFrequencyKey))
|
||||
val frequencySpecifier = ClockGroupFrequencySpecifier(p(ClockFrequencyAssignersKey))
|
||||
val clockGroupCombiner = ClockGroupCombiner()
|
||||
val resetSynchronizer = ClockGroupResetSynchronizer()
|
||||
val tileClockGater = if (prciParams.enableTileClockGating) { prci_ctrl_domain {
|
||||
TileClockGater(prciParams.baseAddress + 0x00000, tlbus)
|
||||
} } else { ClockGroupEphemeralNode() }
|
||||
val tileResetSetter = if (prciParams.enableTileResetSetting) { prci_ctrl_domain {
|
||||
TileResetSetter(prciParams.baseAddress + 0x10000, tlbus, tile_prci_domains.map(_.tile_reset_domain.clockNode.portParams(0).name.get), Nil)
|
||||
} } else { ClockGroupEphemeralNode() }
|
||||
val resetSynchronizer = prci_ctrl_domain {
|
||||
if (prciParams.enableResetSynchronizers) ClockGroupResetSynchronizer() else ClockGroupFakeResetSynchronizer()
|
||||
}
|
||||
val tileClockGater = Option.when(prciParams.enableTileClockGating) { prci_ctrl_domain {
|
||||
val clock_gater = LazyModule(new TileClockGater(prciParams.baseAddress + 0x00000, tlbus.beatBytes))
|
||||
clock_gater.tlNode := TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := prci_ctrl_bus.get
|
||||
clock_gater
|
||||
} }
|
||||
val tileResetSetter = Option.when(prciParams.enableTileResetSetting) { prci_ctrl_domain {
|
||||
val reset_setter = LazyModule(new TileResetSetter(prciParams.baseAddress + 0x10000, tlbus.beatBytes,
|
||||
tile_prci_domains.map(_.tile_reset_domain.clockNode.portParams(0).name.get), Nil))
|
||||
reset_setter.tlNode := TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := prci_ctrl_bus.get
|
||||
reset_setter
|
||||
} }
|
||||
|
||||
if (!prciParams.enableResetSynchronizers) {
|
||||
println(Console.RED + s"""
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
WARNING:
|
||||
|
||||
DISABLING THE RESET SYNCHRONIZERS RESULTS IN
|
||||
A BROKEN DESIGN THAT WILL NOT BEHAVE
|
||||
PROPERLY AS ASIC OR FPGA.
|
||||
|
||||
THESE SHOULD ONLY BE DISABLED TO WORK AROUND
|
||||
LIMITATIONS IN ASYNC RESET INITIALIZATION IN
|
||||
RTL SIMULATORS, NAMELY VERILATOR.
|
||||
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
""" + Console.RESET)
|
||||
}
|
||||
|
||||
(aggregator
|
||||
:= frequencySpecifier
|
||||
:= clockGroupCombiner
|
||||
:= resetSynchronizer
|
||||
:= tileClockGater
|
||||
:= tileResetSetter
|
||||
:= tileClockGater.map(_.clockNode).getOrElse(ClockGroupEphemeralNode()(ValName("temp")))
|
||||
:= tileResetSetter.map(_.clockNode).getOrElse(ClockGroupEphemeralNode()(ValName("temp")))
|
||||
:= allClockGroupsNode)
|
||||
}
|
||||
|
||||
@@ -26,20 +26,26 @@ class TLClockDivider(address: BigInt, beatBytes: Int, divBits: Int = 8)(implicit
|
||||
val sinks = clockNode.out.head._1.member.elements.toSeq
|
||||
require (sources.size == sinks.size)
|
||||
val nSinks = sinks.size
|
||||
// The implicit clock of this module is the clock of the tilelink bus
|
||||
// busReset is sync'd to that clock, and will be asserted longer than the
|
||||
// resets coming in through the clockNode, since the busReset is derived from
|
||||
// the clockNode resets in downstream PRCI nodes
|
||||
val busReset = reset
|
||||
|
||||
val regs = (0 until nSinks) .map { i =>
|
||||
val sinkName = sinks(i)._1
|
||||
val asyncReset = sources(i).reset
|
||||
val reg = withReset (asyncReset) {
|
||||
Module(new AsyncResetRegVec(w=divBits, init=0))
|
||||
}
|
||||
val reg = Module(new AsyncResetRegVec(w=divBits, init=0))
|
||||
|
||||
println(s"${(address+i*4).toString(16)}: Clock domain $sinkName divider")
|
||||
sinks(i)._2.clock := withClockAndReset(sources(i).clock, asyncReset) {
|
||||
val divider = Module(new testchipip.ClockDivideOrPass(divBits, depth = 3, genClockGate = p(ClockGateImpl)))
|
||||
divider.io.divisor := reg.io.q
|
||||
divider.io.resetAsync := ResetStretcher(sources(i).clock, asyncReset, 20).asAsyncReset
|
||||
divider.io.clockOut
|
||||
}
|
||||
val divider = Module(new testchipip.ClockDivideOrPass(divBits, depth = 3, genClockGate = p(ClockGateImpl)))
|
||||
divider.io.clockIn := sources(i).clock
|
||||
// busReset is expected to be high for a long time, since reset will take a while to propagate
|
||||
// to the TL bus. While reset is propagating, make sure we propagate a fast, undivided clock
|
||||
// by setting divisor=0. The divisor signal into the ClockDividerOrPass is synchronized internally
|
||||
divider.io.divisor := Mux(busReset.asBool, 0.U, reg.io.q)
|
||||
divider.io.resetAsync := ResetStretcher(sources(i).clock, asyncReset, 20).asAsyncReset
|
||||
sinks(i)._2.clock := divider.io.clockOut
|
||||
|
||||
// Note this is not synchronized to the output clock, which takes time to appear
|
||||
// so this is still asyncreset
|
||||
|
||||
@@ -13,23 +13,6 @@ import freechips.rocketchip.util.ElaborationArtefacts
|
||||
|
||||
import testchipip._
|
||||
|
||||
object ResetStretcher {
|
||||
def apply(clock: Clock, reset: Reset, cycles: Int): Reset = {
|
||||
withClockAndReset(clock, reset) {
|
||||
val n = log2Ceil(cycles)
|
||||
val count = Module(new AsyncResetRegVec(w=n, init=0))
|
||||
val resetout = Module(new AsyncResetRegVec(w=1, init=1))
|
||||
count.io.en := resetout.io.q
|
||||
count.io.d := count.io.q + 1.U
|
||||
resetout.io.en := resetout.io.q
|
||||
resetout.io.d := count.io.q < (cycles-1).U
|
||||
|
||||
resetout.io.q.asBool
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
case class ClockSelNode()(implicit valName: ValName)
|
||||
extends MixedNexusNode(ClockImp, ClockGroupImp)(
|
||||
dFn = { d => ClockGroupSourceParameters() },
|
||||
|
||||
@@ -46,10 +46,3 @@ class TileClockGater(address: BigInt, beatBytes: Int)(implicit p: Parameters, va
|
||||
}
|
||||
}
|
||||
|
||||
object TileClockGater {
|
||||
def apply(address: BigInt, tlbus: TLBusWrapper)(implicit p: Parameters, v: ValName) = {
|
||||
val gater = LazyModule(new TileClockGater(address, tlbus.beatBytes))
|
||||
tlbus.toVariableWidthSlave(Some("clock-gater")) { gater.tlNode := TLBuffer() }
|
||||
gater.clockNode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,12 +62,3 @@ class TileResetSetter(address: BigInt, beatBytes: Int, tileNames: Seq[String], i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object TileResetSetter {
|
||||
def apply(address: BigInt, tlbus: TLBusWrapper, tileNames: Seq[String], initResetHarts: Seq[Int])(implicit p: Parameters, v: ValName) = {
|
||||
val setter = LazyModule(new TileResetSetter(address, tlbus.beatBytes, tileNames, initResetHarts))
|
||||
tlbus.toVariableWidthSlave(Some("tile-reset-setter")) { setter.tlNode := TLBuffer() }
|
||||
setter.clockNode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,50 +12,58 @@ import org.chipsalliance.cde.config.{Config}
|
||||
|
||||
class AbstractConfig extends Config(
|
||||
// The HarnessBinders control generation of hardware in the TestHarness
|
||||
new chipyard.harness.WithUARTAdapter ++ // add UART adapter to display UART on stdout, if uart is present
|
||||
new chipyard.harness.WithBlackBoxSimMem ++ // add SimDRAM DRAM model for axi4 backing memory, if axi4 mem is enabled
|
||||
new chipyard.harness.WithSimSerial ++ // add external serial-adapter and RAM
|
||||
new chipyard.harness.WithSimDebug ++ // add SimJTAG or SimDTM adapters if debug module is enabled
|
||||
new chipyard.harness.WithGPIOTiedOff ++ // tie-off chiptop GPIOs, if GPIOs are present
|
||||
new chipyard.harness.WithSimSPIFlashModel ++ // add simulated SPI flash memory, if SPI is enabled
|
||||
new chipyard.harness.WithSimAXIMMIO ++ // add SimAXIMem for axi4 mmio port, if enabled
|
||||
new chipyard.harness.WithTieOffInterrupts ++ // tie-off interrupt ports, if present
|
||||
new chipyard.harness.WithTieOffL2FBusAXI ++ // tie-off external AXI4 master, if present
|
||||
new chipyard.harness.WithCustomBootPinPlusArg ++
|
||||
new chipyard.harness.WithClockAndResetFromHarness ++
|
||||
new chipyard.harness.WithUARTAdapter ++ // add UART adapter to display UART on stdout, if uart is present
|
||||
new chipyard.harness.WithBlackBoxSimMem ++ // add SimDRAM DRAM model for axi4 backing memory, if axi4 mem is enabled
|
||||
new chipyard.harness.WithSimTSIOverSerialTL ++ // add external serial-adapter and RAM
|
||||
new chipyard.harness.WithSimDebug ++ // add SimJTAG or SimDTM adapters if debug module is enabled
|
||||
new chipyard.harness.WithGPIOTiedOff ++ // tie-off chiptop GPIOs, if GPIOs are present
|
||||
new chipyard.harness.WithSimSPIFlashModel ++ // add simulated SPI flash memory, if SPI is enabled
|
||||
new chipyard.harness.WithSimAXIMMIO ++ // add SimAXIMem for axi4 mmio port, if enabled
|
||||
new chipyard.harness.WithTieOffInterrupts ++ // tie-off interrupt ports, if present
|
||||
new chipyard.harness.WithTieOffL2FBusAXI ++ // tie-off external AXI4 master, if present
|
||||
new chipyard.harness.WithCustomBootPinPlusArg ++ // drive custom-boot pin with a plusarg, if custom-boot-pin is present
|
||||
new chipyard.harness.WithSimUARTToUARTTSI ++ // connect a SimUART to the UART-TSI port
|
||||
new chipyard.harness.WithClockAndResetFromHarness ++ // all Clock/Reset I/O in ChipTop should be driven by harnessClockInstantiator
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ // generate clocks in harness with unsynthesizable ClockSourceAtFreqMHz
|
||||
|
||||
// The IOBinders instantiate ChipTop IOs to match desired digital IOs
|
||||
// IOCells are generated for "Chip-like" IOs, while simulation-only IOs are directly punched through
|
||||
// IOCells are generated for "Chip-like" IOs
|
||||
new chipyard.iobinders.WithSerialTLIOCells ++
|
||||
new chipyard.iobinders.WithDebugIOCells ++
|
||||
new chipyard.iobinders.WithUARTIOCells ++
|
||||
new chipyard.iobinders.WithGPIOCells ++
|
||||
new chipyard.iobinders.WithSPIIOCells ++
|
||||
new chipyard.iobinders.WithExtInterruptIOCells ++
|
||||
new chipyard.iobinders.WithCustomBootPin ++
|
||||
// The "punchthrough" IOBInders below don't generate IOCells, as these interfaces shouldn't really be mapped to ASIC IO
|
||||
// Instead, they directly pass through the DigitalTop ports to ports in the ChipTop
|
||||
new chipyard.iobinders.WithAXI4MemPunchthrough ++
|
||||
new chipyard.iobinders.WithAXI4MMIOPunchthrough ++
|
||||
new chipyard.iobinders.WithTLMemPunchthrough ++
|
||||
new chipyard.iobinders.WithL2FBusAXI4Punchthrough ++
|
||||
new chipyard.iobinders.WithBlockDeviceIOPunchthrough ++
|
||||
new chipyard.iobinders.WithNICIOPunchthrough ++
|
||||
new chipyard.iobinders.WithSerialTLIOCells ++
|
||||
new chipyard.iobinders.WithDebugIOCells ++
|
||||
new chipyard.iobinders.WithUARTIOCells ++
|
||||
new chipyard.iobinders.WithGPIOCells ++
|
||||
new chipyard.iobinders.WithSPIIOCells ++
|
||||
new chipyard.iobinders.WithTraceIOPunchthrough ++
|
||||
new chipyard.iobinders.WithExtInterruptIOCells ++
|
||||
new chipyard.iobinders.WithCustomBootPin ++
|
||||
new chipyard.iobinders.WithUARTTSIPunchthrough ++
|
||||
|
||||
// Default behavior is to use a divider-only clock-generator
|
||||
// This works in VCS, Verilator, and FireSim/
|
||||
// This should get replaced with a PLL-like config instead
|
||||
new chipyard.clocking.WithDividerOnlyClockGenerator ++
|
||||
// By default, punch out IOs to the Harness
|
||||
new chipyard.clocking.WithPassthroughClockGenerator ++
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName(("uncore", Seq("sbus", "mbus", "pbus", "fbus", "cbus", "implicit"), Seq("tile"))) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(500.0) ++ // Default 500 MHz pbus
|
||||
new chipyard.config.WithMemoryBusFrequency(500.0) ++ // Default 500 MHz mbus
|
||||
|
||||
new testchipip.WithCustomBootPin ++ // add a custom-boot-pin to support pin-driven boot address
|
||||
new testchipip.WithBootAddrReg ++ // add a boot-addr-reg for configurable boot address
|
||||
new testchipip.WithSerialTLClientIdBits(4) ++ // support up to 1 << 4 simultaneous requests from serialTL port
|
||||
new testchipip.WithSerialTLWidth(32) ++ // fatten the serialTL interface to improve testing performance
|
||||
new testchipip.WithDefaultSerialTL ++ // use serialized tilelink port to external serialadapter/harnessRAM
|
||||
new chipyard.config.WithDebugModuleAbstractDataWords(8) ++ // increase debug module data capacity
|
||||
new chipyard.config.WithBootROM ++ // use default bootrom
|
||||
new chipyard.config.WithUART ++ // add a UART
|
||||
new chipyard.config.WithL2TLBs(1024) ++ // use L2 TLBs
|
||||
new chipyard.config.WithNoSubsystemDrivenClocks ++ // drive the subsystem diplomatic clocks from ChipTop instead of using implicit clocks
|
||||
new chipyard.config.WithInheritBusFrequencyAssignments ++ // Unspecified clocks within a bus will receive the bus frequency if set
|
||||
new chipyard.config.WithPeripheryBusFrequencyAsDefault ++ // Unspecified frequencies with match the pbus frequency (which is always set)
|
||||
new chipyard.config.WithMemoryBusFrequency(100.0) ++ // Default 100 MHz mbus
|
||||
new chipyard.config.WithPeripheryBusFrequency(100.0) ++ // Default 100 MHz pbus
|
||||
new freechips.rocketchip.subsystem.WithNMemoryChannels(1) ++ // Default 1 memory channels
|
||||
new freechips.rocketchip.subsystem.WithClockGateModel ++ // add default EICG_wrapper clock gate model
|
||||
new freechips.rocketchip.subsystem.WithJtagDTM ++ // set the debug module to expose a JTAG port
|
||||
new freechips.rocketchip.subsystem.WithNoMMIOPort ++ // no top-level MMIO master port (overrides default set in rocketchip)
|
||||
|
||||
@@ -53,3 +53,18 @@ class MediumBoomCosimConfig extends Config(
|
||||
new chipyard.config.WithTraceIO ++ // enable the traceio
|
||||
new boom.common.WithNMediumBooms(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class dmiMediumBoomConfig extends Config(
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // don't attach anything to serial-tl
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new boom.common.WithNMediumBooms(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class dmiMediumBoomCosimConfig extends Config(
|
||||
new chipyard.harness.WithCospike ++ // attach spike-cosim
|
||||
new chipyard.config.WithTraceIO ++ // enable the traceio
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // don't attach anythint to serial-tl
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new boom.common.WithNMediumBooms(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class CVA6Config extends Config(
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class dmiCVA6Config extends Config(
|
||||
new chipyard.harness.WithSerialAdapterTiedOff ++ // Tie off the serial port, override default instantiation of SimSerial
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // Tie off the serial-tilelink port
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new cva6.WithNCVA6Cores(1) ++ // single CVA6 core
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
@@ -2,43 +2,106 @@ package chipyard
|
||||
|
||||
import org.chipsalliance.cde.config.{Config}
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.subsystem.{MBUS, SBUS}
|
||||
import testchipip.{OBUS}
|
||||
|
||||
// A simple config demonstrating how to set up a basic chip in Chipyard
|
||||
class ChipLikeQuadRocketConfig extends Config(
|
||||
class ChipLikeRocketConfig extends Config(
|
||||
//==================================
|
||||
// Set up TestHarness
|
||||
//==================================
|
||||
new chipyard.WithAbsoluteFreqHarnessClockInstantiator ++ // use absolute frequencies for simulations in the harness
|
||||
// NOTE: This only simulates properly in VCS
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ // use absolute frequencies for simulations in the harness
|
||||
// NOTE: This only simulates properly in VCS
|
||||
new chipyard.harness.WithSimAXIMemOverSerialTL ++ // Attach SimDRAM to serial-tl port
|
||||
|
||||
//==================================
|
||||
// Set up tiles
|
||||
//==================================
|
||||
new freechips.rocketchip.subsystem.WithAsynchronousRocketTiles(3, 3) ++ // Add rational crossings between RocketTile and uncore
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(4) ++ // quad-core (4 RocketTiles)
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // 1 RocketTile
|
||||
|
||||
//==================================
|
||||
// Set up I/O
|
||||
//==================================
|
||||
new testchipip.WithSerialTLWidth(4) ++
|
||||
new chipyard.harness.WithSimAXIMemOverSerialTL ++ // Attach fast SimDRAM to TestHarness
|
||||
new chipyard.config.WithSerialTLBackingMemory ++ // Backing memory is over serial TL protocol
|
||||
new testchipip.WithSerialTLBackingMemory ++ // Backing memory is over serial TL protocol
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 4L) ++ // 4GB max external memory
|
||||
new freechips.rocketchip.subsystem.WithNMemoryChannels(1) ++ // 1 memory channel
|
||||
|
||||
//==================================
|
||||
// Set up buses
|
||||
//==================================
|
||||
new testchipip.WithOffchipBusClient(MBUS) ++
|
||||
new testchipip.WithOffchipBus ++
|
||||
|
||||
//==================================
|
||||
// Set up clock./reset
|
||||
//==================================
|
||||
new chipyard.clocking.WithPLLSelectorDividerClockGenerator ++ // Use a PLL-based clock selector/divider generator structure
|
||||
|
||||
// Create two clock groups, uncore and fbus, in addition to the tile clock groups
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName("uncore", "implicit", "sbus", "mbus", "cbus", "system_bus") ++
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName("fbus", "fbus", "pbus") ++
|
||||
|
||||
// Set up the crossings
|
||||
new chipyard.config.WithFbusToSbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between SBUS and FBUS
|
||||
new chipyard.config.WithCbusToPbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between PBUS and CBUS
|
||||
new chipyard.config.WithSbusToMbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossings between backside of L2 and MBUS
|
||||
new testchipip.WithAsynchronousSerialSlaveCrossing ++ // Add Async crossing between serial and MBUS. Its master-side is tied to the FBUS
|
||||
// Create the uncore clock group
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName(("uncore", Seq("implicit", "sbus", "mbus", "cbus", "system_bus", "fbus", "pbus"), Nil)) ++
|
||||
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
// A simple config demonstrating a "bringup prototype" to bringup the ChipLikeRocketconfig
|
||||
class ChipBringupHostConfig extends Config(
|
||||
//=============================
|
||||
// Set up TestHarness for standalone-sim
|
||||
//=============================
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ // Generate absolute frequencies
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // when doing standalone sim, tie off the serial-tl port
|
||||
new chipyard.harness.WithSimTSIToUARTTSI ++ // Attach SimTSI-over-UART to the UART-TSI port
|
||||
new chipyard.iobinders.WithSerialTLPunchthrough ++ // Don't generate IOCells for the serial TL (this design maps to FPGA)
|
||||
|
||||
//=============================
|
||||
// Setup the SerialTL side on the bringup device
|
||||
//=============================
|
||||
new testchipip.WithSerialTLWidth(4) ++ // match width with the chip
|
||||
new testchipip.WithSerialTLMem(base = 0x0, size = 0x80000000L, // accessible memory of the chip that doesn't come from the tethered host
|
||||
idBits = 4, isMainMemory = false) ++ // This assumes off-chip mem starts at 0x8000_0000
|
||||
new testchipip.WithSerialTLClockDirection(provideClockFreqMHz = Some(75)) ++ // bringup board drives the clock for the serial-tl receiver on the chip, use 75MHz clock
|
||||
|
||||
//============================
|
||||
// Setup bus topology on the bringup system
|
||||
//============================
|
||||
new testchipip.WithOffchipBusClient(SBUS, // offchip bus hangs off the SBUS
|
||||
blockRange = AddressSet.misaligned(0x80000000L, (BigInt(1) << 30) * 4)) ++ // offchip bus should not see the main memory of the testchip, since that can be accessed directly
|
||||
new testchipip.WithOffchipBus ++ // offchip bus
|
||||
|
||||
//=============================
|
||||
// Set up memory on the bringup system
|
||||
//=============================
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 4L) ++ // match what the chip believes the max size should be
|
||||
|
||||
//=============================
|
||||
// Generate the TSI-over-UART side of the bringup system
|
||||
//=============================
|
||||
new testchipip.WithUARTTSIClient(initBaudRate = BigInt(921600)) ++ // nonstandard baud rate to improve performance
|
||||
|
||||
//=============================
|
||||
// Set up clocks of the bringup system
|
||||
//=============================
|
||||
new chipyard.clocking.WithPassthroughClockGenerator ++ // pass all the clocks through, since this isn't a chip
|
||||
new chipyard.config.WithFrontBusFrequency(75.0) ++ // run all buses of this system at 75 MHz
|
||||
new chipyard.config.WithMemoryBusFrequency(75.0) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(75.0) ++
|
||||
|
||||
// Base is the no-cores config
|
||||
new chipyard.NoCoresConfig)
|
||||
|
||||
class TetheredChipLikeRocketConfig extends Config(
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ // use absolute freqs for sims in the harness
|
||||
new chipyard.harness.WithMultiChipSerialTL(0, 1) ++ // connect the serial-tl ports of the chips together
|
||||
new chipyard.harness.WithMultiChip(0, new ChipLikeRocketConfig) ++
|
||||
new chipyard.harness.WithMultiChip(1, new ChipBringupHostConfig))
|
||||
|
||||
|
||||
// Verilator does not initialize some of the async-reset reset-synchronizer
|
||||
// flops properly, so this config disables them.
|
||||
// This config should only be used for verilator simulations
|
||||
class VerilatorCITetheredChipLikeRocketConfig extends Config(
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++ // use absolute freqs for sims in the harness
|
||||
new chipyard.harness.WithMultiChipSerialTL(0, 1) ++ // connect the serial-tl ports of the chips together
|
||||
new chipyard.harness.WithMultiChip(0, new chipyard.config.WithNoResetSynchronizers ++ new ChipLikeRocketConfig) ++
|
||||
new chipyard.harness.WithMultiChip(1, new ChipBringupHostConfig))
|
||||
|
||||
@@ -61,7 +61,6 @@ class LargeNVDLARocketConfig extends Config(
|
||||
class ManyMMIOAcceleratorRocketConfig extends Config(
|
||||
new chipyard.iobinders.WithDontTouchIOBinders(false) ++ // TODO: hack around dontTouch not working in SFC
|
||||
new fftgenerator.WithFFTGenerator(numPoints=8, width=16, decPt=8) ++ // add 8-point mmio fft at the default addr (0x2400) with 16bit fixed-point numbers.
|
||||
new nvidia.blocks.dla.WithNVDLA("small") ++ // add a small NVDLA
|
||||
new chipyard.example.WithStreamingPassthrough ++ // use top with tilelink-controlled streaming passthrough
|
||||
new chipyard.example.WithStreamingFIR ++ // use top with tilelink-controlled streaming FIR
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
|
||||
|
||||
@@ -68,9 +68,9 @@ class MultiNoCConfig extends Config(
|
||||
"serial-tl" -> 0),
|
||||
outNodeMapping = ListMap(
|
||||
"error" -> 1, "l2[0]" -> 2, "pbus" -> 3, "plic" -> 4,
|
||||
"clint" -> 5, "dmInner" -> 6, "bootrom" -> 7, "tileClockGater" -> 8, "tileResetSetter" -> 9)),
|
||||
"clint" -> 5, "dmInner" -> 6, "bootrom" -> 7, "clock" -> 8)),
|
||||
NoCParams(
|
||||
topology = TerminalRouter(BidirectionalLine(10)),
|
||||
topology = TerminalRouter(BidirectionalLine(9)),
|
||||
channelParamGen = (a, b) => UserChannelParams(Seq.fill(5) { UserVirtualChannelParams(4) }),
|
||||
routingRelation = NonblockingVirtualSubnetworksRouting(TerminalRouterRouting(BidirectionalLineRouting()), 5, 1))
|
||||
)) ++
|
||||
|
||||
@@ -4,6 +4,15 @@ import org.chipsalliance.cde.config.{Config}
|
||||
|
||||
// A empty config with no cores. Useful for testing
|
||||
class NoCoresConfig extends Config(
|
||||
new testchipip.WithNoBootAddrReg ++
|
||||
new testchipip.WithNoCustomBootPin ++
|
||||
new chipyard.config.WithNoCLINT ++
|
||||
new chipyard.config.WithNoBootROM ++
|
||||
new chipyard.config.WithBroadcastManager ++
|
||||
new chipyard.config.WithNoUART ++
|
||||
new chipyard.config.WithNoTileClockGaters ++
|
||||
new chipyard.config.WithNoTileResetSetters ++
|
||||
new chipyard.config.WithNoBusErrorDevices ++
|
||||
new chipyard.config.WithNoDebug ++
|
||||
new chipyard.config.WithNoPLIC ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
@@ -2,6 +2,7 @@ package chipyard
|
||||
|
||||
import org.chipsalliance.cde.config.{Config}
|
||||
import freechips.rocketchip.diplomacy.{AsynchronousCrossing}
|
||||
import freechips.rocketchip.subsystem.{MBUS}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Configs which add non-default peripheral devices or ports
|
||||
@@ -58,20 +59,37 @@ class LBWIFRocketConfig extends Config(
|
||||
|
||||
// DOC include start: DmiRocket
|
||||
class dmiRocketConfig extends Config(
|
||||
new chipyard.harness.WithSerialAdapterTiedOff ++ // don't attach an external SimSerial
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // don't attach anything to serial-tl
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
// DOC include end: DmiRocket
|
||||
|
||||
class ManyPeripheralsRocketConfig extends Config(
|
||||
new testchipip.WithBlockDevice ++ // add block-device module to peripherybus
|
||||
new testchipip.WithOffchipBusClient(MBUS) ++ // OBUS provides backing memory to the MBUS
|
||||
new testchipip.WithOffchipBus ++ // OBUS must exist for serial-tl to master off-chip memory
|
||||
new testchipip.WithSerialTLMem(isMainMemory=true) ++ // set lbwif memory base to DRAM_BASE, use as main memory
|
||||
new chipyard.harness.WithSimSPIFlashModel(true) ++ // add the SPI flash model in the harness (read-only)
|
||||
new chipyard.harness.WithSimBlockDevice ++ // drive block-device IOs with SimBlockDevice
|
||||
new chipyard.config.WithSPIFlash ++ // add the SPI flash controller
|
||||
new freechips.rocketchip.subsystem.WithDefaultMMIOPort ++ // add default external master port
|
||||
new freechips.rocketchip.subsystem.WithDefaultSlavePort ++ // add default external slave port
|
||||
new testchipip.WithBlockDevice ++ // add block-device module to peripherybus
|
||||
new testchipip.WithSerialTLMem(isMainMemory=true) ++ // set lbwif memory base to DRAM_BASE, use as main memory
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // remove AXI4 backing memory
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class QuadChannelRocketConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithNMemoryChannels(4) ++ // 4 AXI4 channels
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class UARTTSIRocketConfig extends Config(
|
||||
new chipyard.harness.WithSerialTLTiedOff ++
|
||||
new testchipip.WithUARTTSIClient ++
|
||||
new chipyard.config.WithMemoryBusFrequency(10) ++
|
||||
new chipyard.config.WithFrontBusFrequency(10) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(10) ++
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // single rocket-core
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
|
||||
@@ -29,14 +29,6 @@ class RocketGPUConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithNCustomSmallCores(2) ++ // multiple rocket-core
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class UARTTSIRocketConfig extends Config(
|
||||
new chipyard.harness.WithUARTSerial ++
|
||||
new chipyard.config.WithNoUART ++
|
||||
new chipyard.config.WithMemoryBusFrequency(10) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(10) ++
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++ // single rocket-core
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class SimAXIRocketConfig extends Config(
|
||||
new chipyard.harness.WithSimAXIMem ++ // drive the master AXI4 memory with a SimAXIMem, a 1-cycle magic memory, instead of default SimDRAM
|
||||
new freechips.rocketchip.subsystem.WithNBigCores(1) ++
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package chipyard
|
||||
|
||||
import org.chipsalliance.cde.config.{Config}
|
||||
|
||||
//-----------------
|
||||
// Shuttle Configs
|
||||
//-----------------
|
||||
|
||||
class ShuttleConfig extends Config(
|
||||
new shuttle.common.WithNShuttleCores ++ // 1x dual-issue shuttle core
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class ShuttleCosimConfig extends Config(
|
||||
new chipyard.harness.WithCospike ++ // attach spike-cosim
|
||||
new chipyard.config.WithTraceIO ++ // enable trace-io for cosim
|
||||
new shuttle.common.WithShuttleDebugROB ++ // enable shuttle debug ROB for cosim
|
||||
new shuttle.common.WithNShuttleCores ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class dmiShuttleCosimConfig extends Config(
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // don't attach anything to serial-tl
|
||||
new chipyard.harness.WithCospike ++ // attach spike-cosim
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new chipyard.config.WithTraceIO ++ // enable traceio for cosim
|
||||
new shuttle.common.WithShuttleDebugROB ++ // enable shuttle debug ROB for cosim
|
||||
new shuttle.common.WithNShuttleCores ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class GemminiShuttleConfig extends Config(
|
||||
new gemmini.DefaultGemminiConfig ++ // use Gemmini systolic array GEMM accel
|
||||
new shuttle.common.WithNShuttleCores ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
@@ -8,7 +8,6 @@ class Sodor1StageConfig extends Config(
|
||||
// Create a Sodor 1-stage core
|
||||
new sodor.common.WithNSodorCores(1, internalTile = sodor.common.Stage1Factory) ++
|
||||
new testchipip.WithSerialTLWidth(32) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new freechips.rocketchip.subsystem.WithScratchpadsOnly ++ // use sodor tile-internal scratchpad
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // use no external memory
|
||||
new freechips.rocketchip.subsystem.WithNBanks(0) ++
|
||||
@@ -18,7 +17,6 @@ class Sodor2StageConfig extends Config(
|
||||
// Create a Sodor 2-stage core
|
||||
new sodor.common.WithNSodorCores(1, internalTile = sodor.common.Stage2Factory) ++
|
||||
new testchipip.WithSerialTLWidth(32) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new freechips.rocketchip.subsystem.WithScratchpadsOnly ++ // use sodor tile-internal scratchpad
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // use no external memory
|
||||
new freechips.rocketchip.subsystem.WithNBanks(0) ++
|
||||
@@ -28,7 +26,6 @@ class Sodor3StageConfig extends Config(
|
||||
// Create a Sodor 1-stage core with two ports
|
||||
new sodor.common.WithNSodorCores(1, internalTile = sodor.common.Stage3Factory(ports = 2)) ++
|
||||
new testchipip.WithSerialTLWidth(32) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new freechips.rocketchip.subsystem.WithScratchpadsOnly ++ // use sodor tile-internal scratchpad
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // use no external memory
|
||||
new freechips.rocketchip.subsystem.WithNBanks(0) ++
|
||||
@@ -38,7 +35,6 @@ class Sodor3StageSinglePortConfig extends Config(
|
||||
// Create a Sodor 3-stage core with one ports (instruction and data memory access controlled by arbiter)
|
||||
new sodor.common.WithNSodorCores(1, internalTile = sodor.common.Stage3Factory(ports = 1)) ++
|
||||
new testchipip.WithSerialTLWidth(32) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new freechips.rocketchip.subsystem.WithScratchpadsOnly ++ // use sodor tile-internal scratchpad
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // use no external memory
|
||||
new freechips.rocketchip.subsystem.WithNBanks(0) ++
|
||||
@@ -48,7 +44,6 @@ class Sodor5StageConfig extends Config(
|
||||
// Create a Sodor 5-stage core
|
||||
new sodor.common.WithNSodorCores(1, internalTile = sodor.common.Stage5Factory) ++
|
||||
new testchipip.WithSerialTLWidth(32) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new freechips.rocketchip.subsystem.WithScratchpadsOnly ++ // use sodor tile-internal scratchpad
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // use no external memory
|
||||
new freechips.rocketchip.subsystem.WithNBanks(0) ++
|
||||
@@ -58,7 +53,6 @@ class SodorUCodeConfig extends Config(
|
||||
// Construct a Sodor microcode-based single-bus core
|
||||
new sodor.common.WithNSodorCores(1, internalTile = sodor.common.UCodeFactory) ++
|
||||
new testchipip.WithSerialTLWidth(32) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new freechips.rocketchip.subsystem.WithScratchpadsOnly ++ // use sodor tile-internal scratchpad
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // use no external memory
|
||||
new freechips.rocketchip.subsystem.WithNBanks(0) ++
|
||||
|
||||
@@ -10,6 +10,11 @@ class SpikeConfig extends Config(
|
||||
new chipyard.WithNSpikeCores(1) ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class dmiSpikeConfig extends Config(
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // don't attach anything to serial-tilelink
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new SpikeConfig)
|
||||
|
||||
// Avoids polling on the UART registers
|
||||
class SpikeFastUARTConfig extends Config(
|
||||
new chipyard.WithNSpikeCores(1) ++
|
||||
@@ -22,13 +27,17 @@ class SpikeFastUARTConfig extends Config(
|
||||
class SpikeUltraFastConfig extends Config(
|
||||
new chipyard.WithSpikeTCM ++
|
||||
new chipyard.WithNSpikeCores(1) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new chipyard.config.WithUARTFIFOEntries(128, 128) ++
|
||||
new chipyard.config.WithMemoryBusFrequency(2) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(2) ++
|
||||
new chipyard.config.WithBroadcastManager ++
|
||||
new chipyard.config.AbstractConfig)
|
||||
|
||||
class dmiSpikeUltraFastConfig extends Config(
|
||||
new chipyard.harness.WithSerialTLTiedOff ++ // don't attach anything to serial-tilelink
|
||||
new chipyard.config.WithDMIDTM ++ // have debug module expose a clocked DMI port
|
||||
new SpikeUltraFastConfig)
|
||||
|
||||
// Add the default firechip devices
|
||||
class SpikeUltraFastDevicesConfig extends Config(
|
||||
new chipyard.harness.WithSimBlockDevice ++
|
||||
@@ -38,7 +47,6 @@ class SpikeUltraFastDevicesConfig extends Config(
|
||||
|
||||
new chipyard.WithSpikeTCM ++
|
||||
new chipyard.WithNSpikeCores(1) ++
|
||||
new testchipip.WithSerialPBusMem ++
|
||||
new chipyard.config.WithUARTFIFOEntries(128, 128) ++
|
||||
new chipyard.config.WithMemoryBusFrequency(2) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(2) ++
|
||||
|
||||
@@ -4,21 +4,24 @@ import org.chipsalliance.cde.config.{Config}
|
||||
import freechips.rocketchip.rocket.{DCacheParams}
|
||||
|
||||
class AbstractTraceGenConfig extends Config(
|
||||
new chipyard.harness.WithAbsoluteFreqHarnessClockInstantiator ++
|
||||
new chipyard.harness.WithBlackBoxSimMem ++
|
||||
new chipyard.harness.WithTraceGenSuccess ++
|
||||
new chipyard.harness.WithClockAndResetFromHarness ++
|
||||
new chipyard.iobinders.WithAXI4MemPunchthrough ++
|
||||
new chipyard.iobinders.WithTraceGenSuccessPunchthrough ++
|
||||
new chipyard.clocking.WithDividerOnlyClockGenerator ++
|
||||
new chipyard.clocking.WithPassthroughClockGenerator ++
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName(("uncore", Seq("sbus", "implicit"), Nil)) ++
|
||||
new chipyard.config.WithTracegenSystem ++
|
||||
new chipyard.config.WithNoSubsystemDrivenClocks ++
|
||||
new chipyard.config.WithPeripheryBusFrequencyAsDefault ++
|
||||
new chipyard.config.WithMemoryBusFrequency(100.0) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(100.0) ++
|
||||
new chipyard.config.WithMemoryBusFrequency(1000.0) ++
|
||||
new chipyard.config.WithSystemBusFrequency(1000.0) ++
|
||||
new chipyard.config.WithPeripheryBusFrequency(1000.0) ++
|
||||
new freechips.rocketchip.subsystem.WithCoherentBusTopology ++
|
||||
new freechips.rocketchip.groundtest.GroundTestBaseConfig)
|
||||
|
||||
|
||||
|
||||
class TraceGenConfig extends Config(
|
||||
new tracegen.WithTraceGen()(List.fill(2) { DCacheParams(nMSHRs = 0, nSets = 16, nWays = 2) }) ++
|
||||
new AbstractTraceGenConfig)
|
||||
|
||||
@@ -14,7 +14,6 @@ import freechips.rocketchip.tilelink.{HasTLBusParams}
|
||||
import chipyard._
|
||||
import chipyard.clocking._
|
||||
|
||||
|
||||
// The default RocketChip BaseSubsystem drives its diplomatic clock graph
|
||||
// with the implicit clocks of Subsystem. Don't do that, instead we extend
|
||||
// the diplomacy graph upwards into the ChipTop, where we connect it to
|
||||
@@ -37,14 +36,6 @@ class WithTileFrequency(fMHz: Double, hartId: Option[Int] = None) extends ClockN
|
||||
},
|
||||
fMHz)
|
||||
|
||||
class WithPeripheryBusFrequencyAsDefault extends Config((site, here, up) => {
|
||||
case DefaultClockFrequencyKey => (site(PeripheryBusKey).dtsFrequency.get.toDouble / (1000 * 1000))
|
||||
})
|
||||
|
||||
class WithSystemBusFrequencyAsDefault extends Config((site, here, up) => {
|
||||
case DefaultClockFrequencyKey => (site(SystemBusKey).dtsFrequency.get.toDouble / (1000 * 1000))
|
||||
})
|
||||
|
||||
class BusFrequencyAssignment[T <: HasTLBusParams](re: Regex, key: Field[T]) extends Config((site, here, up) => {
|
||||
case ClockFrequencyAssignersKey => up(ClockFrequencyAssignersKey, site) ++
|
||||
Seq((cName: String) => site(key).dtsFrequency.flatMap { f =>
|
||||
@@ -116,17 +107,14 @@ class WithControlBusFrequency(freqMHz: Double) extends Config((site, here, up) =
|
||||
class WithRationalMemoryBusCrossing extends WithSbusToMbusCrossingType(RationalCrossing(Symmetric))
|
||||
class WithAsynchrousMemoryBusCrossing extends WithSbusToMbusCrossingType(AsynchronousCrossing())
|
||||
|
||||
class WithTestChipBusFreqs extends Config(
|
||||
// Frequency specifications
|
||||
new chipyard.config.WithTileFrequency(1600.0) ++ // Matches the maximum frequency of U540
|
||||
new chipyard.config.WithSystemBusFrequency(800.0) ++ // Put the system bus at a lower freq, representative of ncore working at a lower frequency than the tiles. Same freq as U540
|
||||
new chipyard.config.WithMemoryBusFrequency(1000.0) ++ // 2x the U540 freq (appropriate for a 128b Mbus)
|
||||
new chipyard.config.WithPeripheryBusFrequency(800.0) ++ // Match the sbus and pbus frequency
|
||||
new chipyard.config.WithSystemBusFrequencyAsDefault ++ // All unspecified clock frequencies, notably the implicit clock, will use the sbus freq (800 MHz)
|
||||
// Crossing specifications
|
||||
new chipyard.config.WithCbusToPbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between PBUS and CBUS
|
||||
new chipyard.config.WithSbusToMbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossings between backside of L2 and MBUS
|
||||
new freechips.rocketchip.subsystem.WithRationalRocketTiles ++ // Add rational crossings between RocketTile and uncore
|
||||
new boom.common.WithRationalBoomTiles ++ // Add rational crossings between BoomTile and uncore
|
||||
new testchipip.WithAsynchronousSerialSlaveCrossing // Add Async crossing between serial and MBUS. Its master-side is tied to the FBUS
|
||||
)
|
||||
class WithNoTileClockGaters extends Config((site, here, up) => {
|
||||
case ChipyardPRCIControlKey => up(ChipyardPRCIControlKey).copy(enableTileClockGating = false)
|
||||
})
|
||||
|
||||
class WithNoTileResetSetters extends Config((site, here, up) => {
|
||||
case ChipyardPRCIControlKey => up(ChipyardPRCIControlKey).copy(enableTileResetSetting = false)
|
||||
})
|
||||
|
||||
class WithNoResetSynchronizers extends Config((site, here, up) => {
|
||||
case ChipyardPRCIControlKey => up(ChipyardPRCIControlKey).copy(enableResetSynchronizers = false)
|
||||
})
|
||||
|
||||
@@ -5,8 +5,9 @@ import chisel3._
|
||||
import chisel3.util.{log2Up}
|
||||
|
||||
import org.chipsalliance.cde.config.{Config}
|
||||
import freechips.rocketchip.devices.tilelink.{BootROMLocated, PLICKey}
|
||||
import freechips.rocketchip.devices.debug.{Debug, ExportDebug, DebugModuleKey, DMI}
|
||||
import freechips.rocketchip.devices.tilelink.{BootROMLocated, PLICKey, CLINTKey}
|
||||
import freechips.rocketchip.devices.debug.{Debug, ExportDebug, DebugModuleKey, DMI, JtagDTMKey, JtagDTMConfig}
|
||||
import freechips.rocketchip.diplomacy.{AsynchronousCrossing}
|
||||
import freechips.rocketchip.stage.phases.TargetDirKey
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.tile.{XLen}
|
||||
@@ -14,53 +15,125 @@ import freechips.rocketchip.tile.{XLen}
|
||||
import sifive.blocks.devices.gpio._
|
||||
import sifive.blocks.devices.uart._
|
||||
import sifive.blocks.devices.spi._
|
||||
import sifive.blocks.devices.i2c._
|
||||
|
||||
import testchipip._
|
||||
|
||||
import chipyard.{ExtTLMem}
|
||||
|
||||
// Set the bootrom to the Chipyard bootrom
|
||||
class WithBootROM extends Config((site, here, up) => {
|
||||
/**
|
||||
* Config fragment for adding a BootROM to the SoC
|
||||
*
|
||||
* @param address the address of the BootROM device
|
||||
* @param size the size of the BootROM
|
||||
* @param hang the power-on reset vector, i.e. the program counter will be set to this value on reset
|
||||
* @param contentFileName the path to the BootROM image
|
||||
*/
|
||||
class WithBootROM(address: BigInt = 0x10000, size: Int = 0x10000, hang: BigInt = 0x10040) extends Config((site, here, up) => {
|
||||
case BootROMLocated(x) => up(BootROMLocated(x), site)
|
||||
.map(_.copy(contentFileName = s"${site(TargetDirKey)}/bootrom.rv${site(XLen)}.img"))
|
||||
.map(_.copy(
|
||||
address = address,
|
||||
size = size,
|
||||
hang = hang,
|
||||
contentFileName = s"${site(TargetDirKey)}/bootrom.rv${site(XLen)}.img"
|
||||
))
|
||||
})
|
||||
|
||||
// DOC include start: gpio config fragment
|
||||
class WithGPIO extends Config((site, here, up) => {
|
||||
case PeripheryGPIOKey => Seq(
|
||||
GPIOParams(address = 0x10012000, width = 4, includeIOF = false))
|
||||
/**
|
||||
* Config fragment for adding a GPIO peripheral device to the SoC
|
||||
*
|
||||
* @param address the address of the GPIO device
|
||||
* @param width the number of pins of the GPIO device
|
||||
*/
|
||||
class WithGPIO(address: BigInt = 0x10010000, width: Int = 4) extends Config ((site, here, up) => {
|
||||
case PeripheryGPIOKey => up(PeripheryGPIOKey) ++ Seq(
|
||||
GPIOParams(address = address, width = width, includeIOF = false))
|
||||
})
|
||||
// DOC include end: gpio config fragment
|
||||
|
||||
class WithUART(baudrate: BigInt = 115200) extends Config((site, here, up) => {
|
||||
case PeripheryUARTKey => Seq(
|
||||
UARTParams(address = 0x54000000L, nTxEntries = 256, nRxEntries = 256, initBaudRate = baudrate))
|
||||
})
|
||||
|
||||
/**
|
||||
* Config fragment for removing all UART peripheral devices from the SoC
|
||||
*/
|
||||
class WithNoUART extends Config((site, here, up) => {
|
||||
case PeripheryUARTKey => Nil
|
||||
})
|
||||
|
||||
/**
|
||||
* Config fragment for adding a UART peripheral device to the SoC
|
||||
*
|
||||
* @param address the address of the UART device
|
||||
* @param baudrate the baudrate of the UART device
|
||||
*/
|
||||
class WithUART(baudrate: BigInt = 115200, address: BigInt = 0x10020000) extends Config ((site, here, up) => {
|
||||
case PeripheryUARTKey => up(PeripheryUARTKey) ++ Seq(
|
||||
UARTParams(address = address, nTxEntries = 256, nRxEntries = 256, initBaudRate = baudrate))
|
||||
})
|
||||
|
||||
class WithUARTFIFOEntries(txEntries: Int, rxEntries: Int) extends Config((site, here, up) => {
|
||||
case PeripheryUARTKey => up(PeripheryUARTKey).map(_.copy(nTxEntries = txEntries, nRxEntries = rxEntries))
|
||||
})
|
||||
|
||||
class WithSPIFlash(size: BigInt = 0x10000000) extends Config((site, here, up) => {
|
||||
// Note: the default size matches freedom with the addresses below
|
||||
case PeripherySPIFlashKey => Seq(
|
||||
SPIFlashParams(rAddress = 0x10040000, fAddress = 0x20000000, fSize = size))
|
||||
class WithUARTInitBaudRate(baudrate: BigInt = 115200) extends Config ((site, here, up) => {
|
||||
case PeripheryUARTKey => up(PeripheryUARTKey).map(_.copy(initBaudRate=baudrate))
|
||||
})
|
||||
|
||||
class WithDMIDTM extends Config((site, here, up) => {
|
||||
case ExportDebug => up(ExportDebug, site).copy(protocols = Set(DMI))
|
||||
/**
|
||||
* Config fragment for adding a SPI peripheral device with Execute-in-Place capability to the SoC
|
||||
*
|
||||
* @param address the address of the SPI controller
|
||||
* @param fAddress the address of the Execute-in-Place (XIP) region of the SPI flash memory
|
||||
* @param size the size of the Execute-in-Place (XIP) region of the SPI flash memory
|
||||
*/
|
||||
class WithSPIFlash(size: BigInt = 0x10000000, address: BigInt = 0x10030000, fAddress: BigInt = 0x20000000) extends Config((site, here, up) => {
|
||||
// Note: the default size matches freedom with the addresses below
|
||||
case PeripherySPIFlashKey => up(PeripherySPIFlashKey) ++ Seq(
|
||||
SPIFlashParams(rAddress = address, fAddress = fAddress, fSize = size))
|
||||
})
|
||||
|
||||
/**
|
||||
* Config fragment for adding a SPI peripheral device to the SoC
|
||||
*
|
||||
* @param address the address of the SPI controller
|
||||
*/
|
||||
class WithSPI(address: BigInt = 0x10031000) extends Config((site, here, up) => {
|
||||
case PeripherySPIKey => up(PeripherySPIKey) ++ Seq(
|
||||
SPIParams(rAddress = address))
|
||||
})
|
||||
|
||||
/**
|
||||
* Config fragment for adding a I2C peripheral device to the SoC
|
||||
*
|
||||
* @param address the address of the I2C controller
|
||||
*/
|
||||
class WithI2C(address: BigInt = 0x10040000) extends Config((site, here, up) => {
|
||||
case PeripheryI2CKey => up(PeripheryI2CKey) ++ Seq(
|
||||
I2CParams(address = address, controlXType = AsynchronousCrossing(), intXType = AsynchronousCrossing())
|
||||
)
|
||||
})
|
||||
|
||||
class WithNoDebug extends Config((site, here, up) => {
|
||||
case DebugModuleKey => None
|
||||
})
|
||||
|
||||
class WithTLSerialLocation(masterWhere: TLBusWrapperLocation, slaveWhere: TLBusWrapperLocation) extends Config((site, here, up) => {
|
||||
case SerialTLAttachKey => up(SerialTLAttachKey, site).copy(masterWhere = masterWhere, slaveWhere = slaveWhere)
|
||||
class WithDMIDTM extends Config((site, here, up) => {
|
||||
case ExportDebug => up(ExportDebug, site).copy(protocols = Set(DMI))
|
||||
})
|
||||
|
||||
/**
|
||||
* Config fragment for adding a JTAG Debug Module to the SoC
|
||||
*
|
||||
* @param idcodeVersion the version of the JTAG protocol the Debug Module supports
|
||||
* @param partNum the part number of the Debug Module
|
||||
* @param manufId the 11-bit JEDEC Designer ID of the chip manufacturer
|
||||
* @param debugIdleCycles the number of cycles the Debug Module waits before responding to a request
|
||||
*/
|
||||
class WithJTAGDTMKey(idcodeVersion: Int = 2, partNum: Int = 0x000, manufId: Int = 0x489, debugIdleCycles: Int = 5) extends Config((site, here, up) => {
|
||||
case JtagDTMKey => new JtagDTMConfig (
|
||||
idcodeVersion = idcodeVersion,
|
||||
idcodePartNum = partNum,
|
||||
idcodeManufId = manufId,
|
||||
debugIdleCycles = debugIdleCycles)
|
||||
})
|
||||
|
||||
class WithTLBackingMemory extends Config((site, here, up) => {
|
||||
@@ -68,18 +141,6 @@ class WithTLBackingMemory extends Config((site, here, up) => {
|
||||
case ExtTLMem => up(ExtMem, site) // enable TL backing memory
|
||||
})
|
||||
|
||||
class WithSerialTLBackingMemory extends Config((site, here, up) => {
|
||||
case ExtMem => None
|
||||
case SerialTLKey => up(SerialTLKey, site).map { k => k.copy(
|
||||
memParams = {
|
||||
val memPortParams = up(ExtMem, site).get
|
||||
require(memPortParams.nMemoryChannels == 1)
|
||||
memPortParams.master
|
||||
},
|
||||
isMemoryDevice = true
|
||||
)}
|
||||
})
|
||||
|
||||
class WithExtMemIdBits(n: Int) extends Config((site, here, up) => {
|
||||
case ExtMem => up(ExtMem, site).map(x => x.copy(master = x.master.copy(idBits = n)))
|
||||
})
|
||||
@@ -87,3 +148,23 @@ class WithExtMemIdBits(n: Int) extends Config((site, here, up) => {
|
||||
class WithNoPLIC extends Config((site, here, up) => {
|
||||
case PLICKey => None
|
||||
})
|
||||
|
||||
class WithDebugModuleAbstractDataWords(words: Int = 16) extends Config((site, here, up) => {
|
||||
case DebugModuleKey => up(DebugModuleKey).map(_.copy(nAbstractDataWords=words))
|
||||
})
|
||||
|
||||
class WithNoCLINT extends Config((site, here, up) => {
|
||||
case CLINTKey => None
|
||||
})
|
||||
|
||||
class WithNoBootROM extends Config((site, here, up) => {
|
||||
case BootROMLocated(_) => None
|
||||
})
|
||||
|
||||
class WithNoBusErrorDevices extends Config((site, here, up) => {
|
||||
case SystemBusKey => up(SystemBusKey).copy(errorDevice = None)
|
||||
case ControlBusKey => up(ControlBusKey).copy(errorDevice = None)
|
||||
case PeripheryBusKey => up(PeripheryBusKey).copy(errorDevice = None)
|
||||
case MemoryBusKey => up(MemoryBusKey).copy(errorDevice = None)
|
||||
case FrontBusKey => up(FrontBusKey).copy(errorDevice = None)
|
||||
})
|
||||
|
||||
@@ -9,7 +9,10 @@ import freechips.rocketchip.rocket.{RocketCoreParams, MulDivParams, DCacheParams
|
||||
|
||||
import boom.common.{BoomTileAttachParams}
|
||||
import cva6.{CVA6TileAttachParams}
|
||||
import sodor.common.{SodorTileAttachParams}
|
||||
import ibex.{IbexTileAttachParams}
|
||||
import testchipip._
|
||||
import barf.{TilePrefetchingMasterPortParams}
|
||||
|
||||
class WithL2TLBs(entries: Int) extends Config((site, here, up) => {
|
||||
case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem), site) map {
|
||||
@@ -79,3 +82,17 @@ class WithRocketDCacheScratchpad extends Config((site, here, up) => {
|
||||
}
|
||||
})
|
||||
|
||||
class WithTilePrefetchers extends Config((site, here, up) => {
|
||||
case TilesLocated(InSubsystem) => up(TilesLocated(InSubsystem), site) map {
|
||||
case tp: RocketTileAttachParams => tp.copy(crossingParams = tp.crossingParams.copy(
|
||||
master = TilePrefetchingMasterPortParams(tp.tileParams.hartId, tp.crossingParams.master)))
|
||||
case tp: BoomTileAttachParams => tp.copy(crossingParams = tp.crossingParams.copy(
|
||||
master = TilePrefetchingMasterPortParams(tp.tileParams.hartId, tp.crossingParams.master)))
|
||||
case tp: SodorTileAttachParams => tp.copy(crossingParams = tp.crossingParams.copy(
|
||||
master = TilePrefetchingMasterPortParams(tp.tileParams.hartId, tp.crossingParams.master)))
|
||||
case tp: IbexTileAttachParams => tp.copy(crossingParams = tp.crossingParams.copy(
|
||||
master = TilePrefetchingMasterPortParams(tp.tileParams.hartId, tp.crossingParams.master)))
|
||||
case tp: CVA6TileAttachParams => tp.copy(crossingParams = tp.crossingParams.copy(
|
||||
master = TilePrefetchingMasterPortParams(tp.tileParams.hartId, tp.crossingParams.master)))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.chipsalliance.cde.config._
|
||||
import freechips.rocketchip.diplomacy.{InModuleBody}
|
||||
import barstools.iocell.chisel._
|
||||
import chipyard._
|
||||
import chipyard.harness.{BuildTop}
|
||||
|
||||
// A "custom" IOCell with additional I/O
|
||||
// The IO don't do anything here in this example
|
||||
|
||||
@@ -7,7 +7,7 @@ import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.prci._
|
||||
import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.devices.debug.{ExportDebug, JtagDTMKey, Debug}
|
||||
import freechips.rocketchip.tilelink.{TLBuffer}
|
||||
import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter}
|
||||
import chipyard.{BuildSystem, DigitalTop}
|
||||
import chipyard.clocking._
|
||||
import chipyard.iobinders.{IOCellKey, JTAGChipIO}
|
||||
@@ -33,9 +33,9 @@ class FlatChipTop(implicit p: Parameters) extends LazyModule {
|
||||
val clockSelector = system.prci_ctrl_domain { LazyModule(new TLClockSelector(baseAddress + 0x30000, tlbus.beatBytes)) }
|
||||
val pllCtrl = system.prci_ctrl_domain { LazyModule(new FakePLLCtrl (baseAddress + 0x40000, tlbus.beatBytes)) }
|
||||
|
||||
tlbus.toVariableWidthSlave(Some("clock-div-ctrl")) { clockDivider.tlNode := TLBuffer() }
|
||||
tlbus.toVariableWidthSlave(Some("clock-sel-ctrl")) { clockSelector.tlNode := TLBuffer() }
|
||||
tlbus.toVariableWidthSlave(Some("pll-ctrl")) { pllCtrl.tlNode := TLBuffer() }
|
||||
tlbus.coupleTo("clock-div-ctrl") { clockDivider.tlNode := TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := TLBuffer() := _ }
|
||||
tlbus.coupleTo("clock-sel-ctrl") { clockSelector.tlNode := TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := TLBuffer() := _ }
|
||||
tlbus.coupleTo("pll-ctrl") { pllCtrl.tlNode := TLFragmenter(tlbus.beatBytes, tlbus.blockBytes) := TLBuffer() := _ }
|
||||
|
||||
system.allClockGroupsNode := clockDivider.clockNode := clockSelector.clockNode
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import freechips.rocketchip.util.{PlusArg}
|
||||
import freechips.rocketchip.subsystem.{CacheBlockBytes}
|
||||
import freechips.rocketchip.devices.debug.{SimJTAG}
|
||||
import freechips.rocketchip.jtag.{JTAGIO}
|
||||
import testchipip.{SerialTLKey, SerialAdapter, UARTAdapter, SimDRAM}
|
||||
import chipyard.{BuildTop}
|
||||
import testchipip.{SerialTLKey, UARTAdapter, SimDRAM, TSIHarness, SimTSI}
|
||||
import chipyard.harness.{BuildTop}
|
||||
|
||||
// A "flat" TestHarness that doesn't use IOBinders
|
||||
// use with caution.
|
||||
@@ -40,28 +40,27 @@ class FlatTestHarness(implicit val p: Parameters) extends Module {
|
||||
|
||||
// Serialized TL
|
||||
val sVal = p(SerialTLKey).get
|
||||
require(sVal.axiMemOverSerialTLParams.isDefined)
|
||||
require(sVal.isMemoryDevice)
|
||||
val axiDomainParams = sVal.axiMemOverSerialTLParams.get
|
||||
val serialTLManagerParams = sVal.serialTLManagerParams.get
|
||||
val axiDomainParams = serialTLManagerParams.axiMemOverSerialTLParams.get
|
||||
require(serialTLManagerParams.isMemoryDevice)
|
||||
val memFreq = axiDomainParams.getMemFrequency(lazyDut.system)
|
||||
|
||||
withClockAndReset(clock, reset) {
|
||||
val memOverSerialTLClockBundle = Wire(new ClockBundle(ClockBundleParameters()))
|
||||
memOverSerialTLClockBundle.clock := clock
|
||||
memOverSerialTLClockBundle.reset := reset
|
||||
val serial_bits = SerialAdapter.asyncQueue(dut.serial_tl_pad, clock, reset)
|
||||
val harnessMultiClockAXIRAM = SerialAdapter.connectHarnessMultiClockAXIRAM(
|
||||
val serial_bits = dut.serial_tl_pad.bits
|
||||
dut.serial_tl_pad.clock := clock
|
||||
val harnessMultiClockAXIRAM = TSIHarness.connectMultiClockAXIRAM(
|
||||
lazyDut.system.serdesser.get,
|
||||
serial_bits,
|
||||
memOverSerialTLClockBundle,
|
||||
clock,
|
||||
reset)
|
||||
io.success := SerialAdapter.connectSimSerial(harnessMultiClockAXIRAM.module.io.tsi_ser, clock, reset)
|
||||
io.success := SimTSI.connect(Some(harnessMultiClockAXIRAM.module.io.tsi), clock, reset)
|
||||
|
||||
// connect SimDRAM from the AXI port coming from the harness multi clock axi ram
|
||||
(harnessMultiClockAXIRAM.mem_axi4 zip harnessMultiClockAXIRAM.memNode.edges.in).map { case (axi_port, edge) =>
|
||||
val memSize = sVal.memParams.size
|
||||
(harnessMultiClockAXIRAM.mem_axi4.get zip harnessMultiClockAXIRAM.memNode.get.edges.in).map { case (axi_port, edge) =>
|
||||
val memSize = serialTLManagerParams.memParams.size
|
||||
val memBase = serialTLManagerParams.memParams.base
|
||||
val lineSize = p(CacheBlockBytes)
|
||||
val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), edge.bundle)).suggestName("simdram")
|
||||
val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), memBase, edge.bundle)).suggestName("simdram")
|
||||
mem.io.axi <> axi_port.bits
|
||||
mem.io.clock := axi_port.clock
|
||||
mem.io.reset := axi_port.reset
|
||||
|
||||
@@ -165,17 +165,17 @@ trait CanHavePeripheryGCD { this: BaseSubsystem =>
|
||||
case Some(params) => {
|
||||
if (params.useAXI4) {
|
||||
val gcd = LazyModule(new GCDAXI4(params, pbus.beatBytes)(p))
|
||||
pbus.toSlave(Some(portName)) {
|
||||
pbus.coupleTo(portName) {
|
||||
gcd.node :=
|
||||
AXI4Buffer () :=
|
||||
TLToAXI4 () :=
|
||||
// toVariableWidthSlave doesn't use holdFirstDeny, which TLToAXI4() needsx
|
||||
TLFragmenter(pbus.beatBytes, pbus.blockBytes, holdFirstDeny = true)
|
||||
TLFragmenter(pbus.beatBytes, pbus.blockBytes, holdFirstDeny = true) := _
|
||||
}
|
||||
Some(gcd)
|
||||
} else {
|
||||
val gcd = LazyModule(new GCDTL(params, pbus.beatBytes)(p))
|
||||
pbus.toVariableWidthSlave(Some(portName)) { gcd.node }
|
||||
pbus.coupleTo(portName) { gcd.node := TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _ }
|
||||
Some(gcd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ trait CanHavePeripheryInitZero { this: BaseSubsystem =>
|
||||
|
||||
p(InitZeroKey) .map { k =>
|
||||
val initZero = LazyModule(new InitZero()(p))
|
||||
fbus.fromPort(Some("init-zero"))() := initZero.node
|
||||
fbus.coupleFrom("init-zero") { _ := initZero.node }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ case class MyCoreParams(
|
||||
val useCryptoNIST: Boolean = false
|
||||
val useCryptoSM: Boolean = false
|
||||
val traceHasWdata: Boolean = false
|
||||
val useConditionalZero = false
|
||||
}
|
||||
|
||||
// DOC include start: CanAttachTile
|
||||
|
||||
@@ -203,7 +203,7 @@ trait CanHavePeripheryStreamingFIR extends BaseSubsystem {
|
||||
genOut = FixedPoint(8.W, 3.BP),
|
||||
coeffs = Seq(1.F(0.BP), 2.F(0.BP), 3.F(0.BP)),
|
||||
params = params))
|
||||
pbus.toVariableWidthSlave(Some("streamingFIR")) { streamingFIR.mem.get := TLFIFOFixer() }
|
||||
pbus.coupleTo("streamingFIR") { streamingFIR.mem.get := TLFIFOFixer() := TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _ }
|
||||
Some(streamingFIR)
|
||||
}
|
||||
case None => None
|
||||
|
||||
@@ -132,7 +132,7 @@ trait CanHavePeripheryStreamingPassthrough { this: BaseSubsystem =>
|
||||
val passthrough = p(StreamingPassthroughKey) match {
|
||||
case Some(params) => {
|
||||
val streamingPassthroughChain = LazyModule(new TLStreamingPassthroughChain(params, UInt(32.W)))
|
||||
pbus.toVariableWidthSlave(Some("streamingPassthrough")) { streamingPassthroughChain.mem.get := TLFIFOFixer() }
|
||||
pbus.coupleTo("streamingPassthrough") { streamingPassthroughChain.mem.get := TLFIFOFixer() := TLFragmenter(pbus.beatBytes, pbus.blockBytes) := _ }
|
||||
Some(streamingPassthroughChain)
|
||||
}
|
||||
case None => None
|
||||
|
||||
@@ -30,12 +30,10 @@ import icenet.{CanHavePeripheryIceNIC, SimNetwork, NicLoopback, NICKey, NICIOvon
|
||||
|
||||
import scala.reflect.{ClassTag}
|
||||
|
||||
case object HarnessBinders extends Field[Map[String, (Any, HasHarnessSignalReferences, Seq[Data]) => Unit]](
|
||||
Map[String, (Any, HasHarnessSignalReferences, Seq[Data]) => Unit]().withDefaultValue((t: Any, th: HasHarnessSignalReferences, d: Seq[Data]) => ())
|
||||
)
|
||||
case object HarnessBinders extends Field[HarnessBinderMap](HarnessBinderMapDefault)
|
||||
|
||||
object ApplyHarnessBinders {
|
||||
def apply(th: HasHarnessSignalReferences, sys: LazyModule, portMap: Map[String, Seq[Data]])(implicit p: Parameters): Unit = {
|
||||
def apply(th: HasHarnessInstantiators, sys: LazyModule, portMap: Map[String, Seq[Data]])(implicit p: Parameters): Unit = {
|
||||
val pm = portMap.withDefaultValue(Nil)
|
||||
p(HarnessBinders).foreach { case (s, f) =>
|
||||
f(sys, th, pm(s))
|
||||
@@ -45,29 +43,25 @@ object ApplyHarnessBinders {
|
||||
}
|
||||
|
||||
// The ClassTags here are necessary to overcome issues arising from type erasure
|
||||
class HarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](composer: ((T, S, Seq[U]) => Unit) => (T, S, Seq[U]) => Unit)(implicit systemTag: ClassTag[T], harnessTag: ClassTag[S], portTag: ClassTag[U]) extends Config((site, here, up) => {
|
||||
class HarnessBinder[T, S <: HasHarnessInstantiators, U <: Data](composer: ((T, S, Seq[U]) => Unit) => (T, S, Seq[U]) => Unit)(implicit systemTag: ClassTag[T], harnessTag: ClassTag[S], portTag: ClassTag[U]) extends Config((site, here, up) => {
|
||||
case HarnessBinders => up(HarnessBinders, site) + (systemTag.runtimeClass.toString ->
|
||||
((t: Any, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
|
||||
((t: Any, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
||||
val pts = ports.collect({case p: U => p})
|
||||
require (pts.length == ports.length, s"Port type mismatch between IOBinder and HarnessBinder: ${portTag}")
|
||||
val upfn = up(HarnessBinders, site)(systemTag.runtimeClass.toString)
|
||||
th match {
|
||||
case th: S =>
|
||||
t match {
|
||||
case system: T => composer(upfn)(system, th, pts)
|
||||
case _ =>
|
||||
}
|
||||
(th, t) match {
|
||||
case (th: S, system: T) => composer(upfn)(system, th, pts)
|
||||
case _ =>
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
class OverrideHarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](fn: => (T, S, Seq[U]) => Unit)
|
||||
class OverrideHarnessBinder[T, S <: HasHarnessInstantiators, U <: Data](fn: => (T, S, Seq[U]) => Unit)
|
||||
(implicit tag: ClassTag[T], thtag: ClassTag[S], ptag: ClassTag[U])
|
||||
extends HarnessBinder[T, S, U]((upfn: (T, S, Seq[U]) => Unit) => fn)
|
||||
|
||||
class ComposeHarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](fn: => (T, S, Seq[U]) => Unit)
|
||||
class ComposeHarnessBinder[T, S <: HasHarnessInstantiators, U <: Data](fn: => (T, S, Seq[U]) => Unit)
|
||||
(implicit tag: ClassTag[T], thtag: ClassTag[S], ptag: ClassTag[U])
|
||||
extends HarnessBinder[T, S, U]((upfn: (T, S, Seq[U]) => Unit) => (t, th, p) => {
|
||||
upfn(t, th, p)
|
||||
@@ -76,104 +70,98 @@ class ComposeHarnessBinder[T, S <: HasHarnessSignalReferences, U <: Data](fn: =>
|
||||
|
||||
|
||||
class WithGPIOTiedOff extends OverrideHarnessBinder({
|
||||
(system: HasPeripheryGPIOModuleImp, th: HasHarnessSignalReferences, ports: Seq[Analog]) => {
|
||||
(system: HasPeripheryGPIOModuleImp, th: HasHarnessInstantiators, ports: Seq[Analog]) => {
|
||||
ports.foreach { _ <> AnalogConst(0) }
|
||||
}
|
||||
})
|
||||
|
||||
// DOC include start: WithUARTAdapter
|
||||
class WithUARTAdapter extends OverrideHarnessBinder({
|
||||
(system: HasPeripheryUARTModuleImp, th: HasHarnessSignalReferences, ports: Seq[UARTPortIO]) => {
|
||||
(system: HasPeripheryUARTModuleImp, th: HasHarnessInstantiators, ports: Seq[UARTPortIO]) => {
|
||||
UARTAdapter.connect(ports)(system.p)
|
||||
}
|
||||
})
|
||||
// DOC include end: WithUARTAdapter
|
||||
|
||||
class WithSimSPIFlashModel(rdOnly: Boolean = true) extends OverrideHarnessBinder({
|
||||
(system: HasPeripherySPIFlashModuleImp, th: HasHarnessSignalReferences, ports: Seq[SPIChipIO]) => {
|
||||
SimSPIFlashModel.connect(ports, th.buildtopReset, rdOnly)(system.p)
|
||||
(system: HasPeripherySPIFlashModuleImp, th: HasHarnessInstantiators, ports: Seq[SPIChipIO]) => {
|
||||
SimSPIFlashModel.connect(ports, th.harnessBinderReset, rdOnly)(system.p)
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimBlockDevice extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryBlockDevice, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
||||
(system: CanHavePeripheryBlockDevice, th: HasHarnessInstantiators, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
ports.map { b => SimBlockDevice.connect(b.clock, th.buildtopReset.asBool, Some(b.bits)) }
|
||||
ports.map { b => SimBlockDevice.connect(b.clock, th.harnessBinderReset.asBool, Some(b.bits)) }
|
||||
}
|
||||
})
|
||||
|
||||
class WithBlockDeviceModel extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryBlockDevice, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
||||
(system: CanHavePeripheryBlockDevice, th: HasHarnessInstantiators, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
ports.map { b => withClockAndReset(b.clock, th.buildtopReset) { BlockDeviceModel.connect(Some(b.bits)) } }
|
||||
ports.map { b => BlockDeviceModel.connect(Some(b.bits)) }
|
||||
}
|
||||
})
|
||||
|
||||
class WithLoopbackNIC extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryIceNIC, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[NICIOvonly]]) => {
|
||||
(system: CanHavePeripheryIceNIC, th: HasHarnessInstantiators, ports: Seq[ClockedIO[NICIOvonly]]) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
ports.map { n =>
|
||||
withClockAndReset(n.clock, th.buildtopReset) {
|
||||
NicLoopback.connect(Some(n.bits), p(NICKey))
|
||||
}
|
||||
}
|
||||
ports.map { n => NicLoopback.connect(Some(n.bits), p(NICKey)) }
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimNetwork extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryIceNIC, th: BaseModule with HasHarnessSignalReferences, ports: Seq[ClockedIO[NICIOvonly]]) => {
|
||||
(system: CanHavePeripheryIceNIC, th: BaseModule with HasHarnessInstantiators, ports: Seq[ClockedIO[NICIOvonly]]) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
ports.map { n => SimNetwork.connect(Some(n.bits), n.clock, th.buildtopReset.asBool) }
|
||||
ports.map { n => SimNetwork.connect(Some(n.bits), n.clock, th.harnessBinderReset.asBool) }
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimAXIMem extends OverrideHarnessBinder({
|
||||
(system: CanHaveMasterAXI4MemPort, th: HasHarnessSignalReferences, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
||||
(system: CanHaveMasterAXI4MemPort, th: HasHarnessInstantiators, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
||||
val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
|
||||
(ports zip system.memAXI4Node.edges.in).map { case (port, edge) =>
|
||||
val mem = LazyModule(new SimAXIMem(edge, size=p(ExtMem).get.master.size)(p))
|
||||
withClockAndReset(port.clock, port.reset) {
|
||||
Module(mem.module).suggestName("mem")
|
||||
}
|
||||
Module(mem.module).suggestName("mem")
|
||||
mem.io_axi4.head <> port.bits
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimAXIMemOverSerialTL extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessInstantiators, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
||||
|
||||
p(SerialTLKey).map({ sVal =>
|
||||
require(sVal.axiMemOverSerialTLParams.isDefined)
|
||||
val axiDomainParams = sVal.axiMemOverSerialTLParams.get
|
||||
require(sVal.isMemoryDevice)
|
||||
val serialTLManagerParams = sVal.serialTLManagerParams.get
|
||||
val axiDomainParams = serialTLManagerParams.axiMemOverSerialTLParams.get
|
||||
require(serialTLManagerParams.isMemoryDevice)
|
||||
|
||||
val memFreq = axiDomainParams.getMemFrequency(system.asInstanceOf[HasTileLinkLocations])
|
||||
|
||||
ports.map({ port =>
|
||||
// DOC include start: HarnessClockInstantiatorEx
|
||||
withClockAndReset(th.buildtopClock, th.buildtopReset) {
|
||||
val memOverSerialTLClockBundle = th.harnessClockInstantiator.requestClockBundle("mem_over_serial_tl_clock", memFreq)
|
||||
val serial_bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
|
||||
val harnessMultiClockAXIRAM = SerialAdapter.connectHarnessMultiClockAXIRAM(
|
||||
system.serdesser.get,
|
||||
serial_bits,
|
||||
memOverSerialTLClockBundle,
|
||||
th.buildtopReset)
|
||||
// DOC include end: HarnessClockInstantiatorEx
|
||||
val success = SerialAdapter.connectSimSerial(harnessMultiClockAXIRAM.module.io.tsi_ser, th.buildtopClock, th.buildtopReset.asBool)
|
||||
when (success) { th.success := true.B }
|
||||
val memOverSerialTLClock = th.harnessClockInstantiator.requestClockHz("mem_over_serial_tl_clock", memFreq)
|
||||
val serial_bits = port.bits
|
||||
port.clock := th.harnessBinderClock
|
||||
val harnessMultiClockAXIRAM = TSIHarness.connectMultiClockAXIRAM(
|
||||
system.serdesser.get,
|
||||
serial_bits,
|
||||
memOverSerialTLClock,
|
||||
th.harnessBinderReset)
|
||||
// DOC include end: HarnessClockInstantiatorEx
|
||||
val success = SimTSI.connect(Some(harnessMultiClockAXIRAM.module.io.tsi), th.harnessBinderClock, th.harnessBinderReset.asBool)
|
||||
when (success) { th.success := true.B }
|
||||
|
||||
// connect SimDRAM from the AXI port coming from the harness multi clock axi ram
|
||||
(harnessMultiClockAXIRAM.mem_axi4 zip harnessMultiClockAXIRAM.memNode.edges.in).map { case (axi_port, edge) =>
|
||||
val memSize = sVal.memParams.size
|
||||
val lineSize = p(CacheBlockBytes)
|
||||
val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), edge.bundle)).suggestName("simdram")
|
||||
mem.io.axi <> axi_port.bits
|
||||
mem.io.clock := axi_port.clock
|
||||
mem.io.reset := axi_port.reset
|
||||
}
|
||||
// connect SimDRAM from the AXI port coming from the harness multi clock axi ram
|
||||
(harnessMultiClockAXIRAM.mem_axi4.get zip harnessMultiClockAXIRAM.memNode.get.edges.in).map { case (axi_port, edge) =>
|
||||
val memSize = serialTLManagerParams.memParams.size
|
||||
val memBase = serialTLManagerParams.memParams.base
|
||||
val lineSize = p(CacheBlockBytes)
|
||||
val mem = Module(new SimDRAM(memSize, lineSize, BigInt(memFreq.toLong), memBase, edge.bundle)).suggestName("simdram")
|
||||
mem.io.axi <> axi_port.bits
|
||||
mem.io.clock := axi_port.clock
|
||||
mem.io.reset := axi_port.reset
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -181,13 +169,15 @@ class WithSimAXIMemOverSerialTL extends OverrideHarnessBinder({
|
||||
})
|
||||
|
||||
class WithBlackBoxSimMem(additionalLatency: Int = 0) extends OverrideHarnessBinder({
|
||||
(system: CanHaveMasterAXI4MemPort, th: HasHarnessSignalReferences, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
||||
(system: CanHaveMasterAXI4MemPort, th: HasHarnessInstantiators, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
||||
val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
|
||||
(ports zip system.memAXI4Node.edges.in).map { case (port, edge) =>
|
||||
// TODO FIX: This currently makes each SimDRAM contain the entire memory space
|
||||
val memSize = p(ExtMem).get.master.size
|
||||
val memBase = p(ExtMem).get.master.base
|
||||
val lineSize = p(CacheBlockBytes)
|
||||
val clockFreq = p(MemoryBusKey).dtsFrequency.get
|
||||
val mem = Module(new SimDRAM(memSize, lineSize, clockFreq, edge.bundle)).suggestName("simdram")
|
||||
val mem = Module(new SimDRAM(memSize, lineSize, clockFreq, memBase, edge.bundle)).suggestName("simdram")
|
||||
mem.io.axi <> port.bits
|
||||
// Bug in Chisel implementation. See https://github.com/chipsalliance/chisel3/pull/1781
|
||||
def Decoupled[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
|
||||
@@ -214,7 +204,7 @@ class WithBlackBoxSimMem(additionalLatency: Int = 0) extends OverrideHarnessBind
|
||||
})
|
||||
|
||||
class WithSimAXIMMIO extends OverrideHarnessBinder({
|
||||
(system: CanHaveMasterAXI4MMIOPort, th: HasHarnessSignalReferences, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
||||
(system: CanHaveMasterAXI4MMIOPort, th: HasHarnessInstantiators, ports: Seq[ClockedAndResetIO[AXI4Bundle]]) => {
|
||||
val p: Parameters = chipyard.iobinders.GetSystemParameters(system)
|
||||
(ports zip system.mmioAXI4Node.edges.in).map { case (port, edge) =>
|
||||
val mmio_mem = LazyModule(new SimAXIMem(edge, size = p(ExtBus).get.size)(p))
|
||||
@@ -227,13 +217,13 @@ class WithSimAXIMMIO extends OverrideHarnessBinder({
|
||||
})
|
||||
|
||||
class WithTieOffInterrupts extends OverrideHarnessBinder({
|
||||
(system: HasExtInterruptsModuleImp, th: HasHarnessSignalReferences, ports: Seq[UInt]) => {
|
||||
(system: HasExtInterruptsModuleImp, th: HasHarnessInstantiators, ports: Seq[UInt]) => {
|
||||
ports.foreach { _ := 0.U }
|
||||
}
|
||||
})
|
||||
|
||||
class WithTieOffL2FBusAXI extends OverrideHarnessBinder({
|
||||
(system: CanHaveSlaveAXI4Port, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[AXI4Bundle]]) => {
|
||||
(system: CanHaveSlaveAXI4Port, th: HasHarnessInstantiators, ports: Seq[ClockedIO[AXI4Bundle]]) => {
|
||||
ports.foreach({ p =>
|
||||
p.bits := DontCare
|
||||
p.bits.aw.valid := false.B
|
||||
@@ -246,13 +236,13 @@ class WithTieOffL2FBusAXI extends OverrideHarnessBinder({
|
||||
})
|
||||
|
||||
class WithSimDebug extends OverrideHarnessBinder({
|
||||
(system: HasPeripheryDebug, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
|
||||
(system: HasPeripheryDebug, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
ports.map {
|
||||
case d: ClockedDMIIO =>
|
||||
val dtm_success = WireInit(false.B)
|
||||
when (dtm_success) { th.success := true.B }
|
||||
val dtm = Module(new SimDTM).connect(th.buildtopClock, th.buildtopReset.asBool, d, dtm_success)
|
||||
val dtm = Module(new TestchipSimDTM).connect(th.harnessBinderClock, th.harnessBinderReset.asBool, d, dtm_success)
|
||||
case j: JTAGChipIO =>
|
||||
val dtm_success = WireInit(false.B)
|
||||
when (dtm_success) { th.success := true.B }
|
||||
@@ -262,13 +252,14 @@ class WithSimDebug extends OverrideHarnessBinder({
|
||||
j.TCK := jtag_wire.TCK
|
||||
j.TMS := jtag_wire.TMS
|
||||
j.TDI := jtag_wire.TDI
|
||||
val jtag = Module(new SimJTAG(tickDelay=3)).connect(jtag_wire, th.buildtopClock, th.buildtopReset.asBool, ~(th.buildtopReset.asBool), dtm_success)
|
||||
val jtag = Module(new SimJTAG(tickDelay=3))
|
||||
jtag.connect(jtag_wire, th.harnessBinderClock, th.harnessBinderReset.asBool, ~(th.harnessBinderReset.asBool), dtm_success)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
class WithTiedOffDebug extends OverrideHarnessBinder({
|
||||
(system: HasPeripheryDebug, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
|
||||
(system: HasPeripheryDebug, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
||||
ports.map {
|
||||
case j: JTAGChipIO =>
|
||||
j.TCK := true.B.asClock
|
||||
@@ -294,78 +285,93 @@ class WithTiedOffDebug extends OverrideHarnessBinder({
|
||||
})
|
||||
|
||||
|
||||
class WithSerialAdapterTiedOff extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
class WithSerialTLTiedOff extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessInstantiators, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
||||
ports.map({ port =>
|
||||
val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
|
||||
withClockAndReset(th.buildtopClock, th.buildtopReset) {
|
||||
val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset)
|
||||
SerialAdapter.tieoff(ram.module.io.tsi_ser)
|
||||
val bits = port.bits
|
||||
if (DataMirror.directionOf(port.clock) == Direction.Input) {
|
||||
port.clock := false.B.asClock
|
||||
}
|
||||
port.bits.out.ready := false.B
|
||||
port.bits.in.valid := false.B
|
||||
port.bits.in.bits := DontCare
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimSerial extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
class WithSimTSIOverSerialTL extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessInstantiators, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
||||
ports.map({ port =>
|
||||
val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
|
||||
withClockAndReset(th.buildtopClock, th.buildtopReset) {
|
||||
val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset)
|
||||
val success = SerialAdapter.connectSimSerial(ram.module.io.tsi_ser, th.buildtopClock, th.buildtopReset.asBool)
|
||||
when (success) { th.success := true.B }
|
||||
}
|
||||
val bits = port.bits
|
||||
port.clock := th.harnessBinderClock
|
||||
val ram = TSIHarness.connectRAM(system.serdesser.get, bits, th.harnessBinderReset)
|
||||
val success = SimTSI.connect(Some(ram.module.io.tsi), th.harnessBinderClock, th.harnessBinderReset.asBool)
|
||||
when (success) { th.success := true.B }
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
class WithUARTSerial extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: HasHarnessSignalReferences, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
class WithSimUARTToUARTTSI extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryUARTTSI, th: HasHarnessInstantiators, ports: Seq[UARTTSIIO]) => {
|
||||
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
||||
require(ports.size <= 1)
|
||||
ports.map { port => {
|
||||
UARTAdapter.connect(Seq(port.uart),
|
||||
baudrate=port.uartParams.initBaudRate,
|
||||
clockFrequency=th.getHarnessBinderClockFreqHz.toInt,
|
||||
forcePty=true)
|
||||
assert(!port.dropped)
|
||||
}}
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimTSIToUARTTSI extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryUARTTSI, th: HasHarnessInstantiators, ports: Seq[UARTTSIIO]) => {
|
||||
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
||||
require(ports.size <= 1)
|
||||
ports.map({ port =>
|
||||
val freq = p(PeripheryBusKey).dtsFrequency.get
|
||||
val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
|
||||
withClockAndReset(th.buildtopClock, th.buildtopReset) {
|
||||
val ram = SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset)
|
||||
val uart_to_serial = Module(new UARTToSerial(freq, UARTParams(0)))
|
||||
val serial_width_adapter = Module(new SerialWidthAdapter(
|
||||
8, SerialAdapter.SERIAL_TSI_WIDTH))
|
||||
ram.module.io.tsi_ser.flipConnect(serial_width_adapter.io.wide)
|
||||
UARTAdapter.connect(Seq(uart_to_serial.io.uart), uart_to_serial.div)
|
||||
serial_width_adapter.io.narrow.flipConnect(uart_to_serial.io.serial)
|
||||
th.success := false.B
|
||||
}
|
||||
val freq = th.getHarnessBinderClockFreqHz.toInt
|
||||
val uart_to_serial = Module(new UARTToSerial(freq, port.uartParams))
|
||||
val serial_width_adapter = Module(new SerialWidthAdapter(8, TSI.WIDTH))
|
||||
val success = SimTSI.connect(Some(TSIIO(serial_width_adapter.io.wide)), th.harnessBinderClock, th.harnessBinderReset)
|
||||
when (success) { th.success := true.B }
|
||||
assert(!uart_to_serial.io.dropped)
|
||||
serial_width_adapter.io.narrow.flipConnect(uart_to_serial.io.serial)
|
||||
uart_to_serial.io.uart.rxd := port.uart.txd
|
||||
port.uart.rxd := uart_to_serial.io.uart.txd
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
class WithTraceGenSuccess extends OverrideHarnessBinder({
|
||||
(system: TraceGenSystemModuleImp, th: HasHarnessSignalReferences, ports: Seq[Bool]) => {
|
||||
(system: TraceGenSystemModuleImp, th: HasHarnessInstantiators, ports: Seq[Bool]) => {
|
||||
ports.map { p => when (p) { th.success := true.B } }
|
||||
}
|
||||
})
|
||||
|
||||
class WithSimDromajoBridge extends ComposeHarnessBinder({
|
||||
(system: CanHaveTraceIOModuleImp, th: HasHarnessSignalReferences, ports: Seq[TraceOutputTop]) => {
|
||||
(system: CanHaveTraceIOModuleImp, th: HasHarnessInstantiators, ports: Seq[TraceOutputTop]) => {
|
||||
ports.map { p => p.traces.map(tileTrace => SimDromajoBridge(tileTrace)(system.p)) }
|
||||
}
|
||||
})
|
||||
|
||||
class WithCospike extends ComposeHarnessBinder({
|
||||
(system: CanHaveTraceIOModuleImp, th: HasHarnessSignalReferences, ports: Seq[TraceOutputTop]) => {
|
||||
(system: CanHaveTraceIOModuleImp, th: HasHarnessInstantiators, ports: Seq[TraceOutputTop]) => {
|
||||
implicit val p = chipyard.iobinders.GetSystemParameters(system)
|
||||
val chipyardSystem = system.asInstanceOf[ChipyardSystemModule[_]].outer.asInstanceOf[ChipyardSystem]
|
||||
val tiles = chipyardSystem.tiles
|
||||
val cfg = SpikeCosimConfig(
|
||||
isa = tiles.headOption.map(_.isaDTS).getOrElse(""),
|
||||
priv = tiles.headOption.map(t => if (t.usingUser) "MSU" else if (t.usingSupervisor) "MS" else "M").getOrElse(""),
|
||||
mem0_base = p(ExtMem).map(_.master.base).getOrElse(BigInt(0)),
|
||||
mem0_size = p(ExtMem).map(_.master.size).getOrElse(BigInt(0)),
|
||||
pmpregions = tiles.headOption.map(_.tileParams.core.nPMPs).getOrElse(0),
|
||||
nharts = tiles.size,
|
||||
bootrom = chipyardSystem.bootROM.map(_.module.contents.toArray.mkString(" ")).getOrElse("")
|
||||
bootrom = chipyardSystem.bootROM.map(_.module.contents.toArray.mkString(" ")).getOrElse(""),
|
||||
has_dtm = p(ExportDebug).protocols.contains(DMI) // assume that exposing clockeddmi means we will connect SimDTM
|
||||
)
|
||||
ports.map { p => p.traces.zipWithIndex.map(t => SpikeCosim(t._1, t._2, cfg)) }
|
||||
}
|
||||
@@ -373,7 +379,7 @@ class WithCospike extends ComposeHarnessBinder({
|
||||
|
||||
|
||||
class WithCustomBootPinPlusArg extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryCustomBootPin, th: HasHarnessSignalReferences, ports: Seq[Bool]) => {
|
||||
(system: CanHavePeripheryCustomBootPin, th: HasHarnessInstantiators, ports: Seq[Bool]) => {
|
||||
val pin = PlusArg("custom_boot_pin", width=1)
|
||||
ports.foreach(_ := pin)
|
||||
}
|
||||
@@ -381,14 +387,15 @@ class WithCustomBootPinPlusArg extends OverrideHarnessBinder({
|
||||
|
||||
|
||||
class WithClockAndResetFromHarness extends OverrideHarnessBinder({
|
||||
(system: HasChipyardPRCI, th: HasHarnessSignalReferences, ports: Seq[Data]) => {
|
||||
(system: HasChipyardPRCI, th: HasHarnessInstantiators, ports: Seq[Data]) => {
|
||||
implicit val p = GetSystemParameters(system)
|
||||
val clocks = ports.collect { case c: ClockWithFreq => c }
|
||||
ports.map ({
|
||||
case c: ClockWithFreq => {
|
||||
th.setRefClockFreq(c.freqMHz)
|
||||
c.clock := th.buildtopClock
|
||||
val clock = th.harnessClockInstantiator.requestClockMHz(s"clock_${c.freqMHz.toInt}MHz", c.freqMHz)
|
||||
c.clock := clock
|
||||
}
|
||||
case r: AsyncReset => r := th.buildtopReset.asAsyncReset
|
||||
case r: AsyncReset => r := th.referenceReset.asAsyncReset
|
||||
})
|
||||
}
|
||||
})
|
||||
100
generators/chipyard/src/main/scala/harness/HarnessClocks.scala
Normal file
100
generators/chipyard/src/main/scala/harness/HarnessClocks.scala
Normal file
@@ -0,0 +1,100 @@
|
||||
package chipyard.harness
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import chisel3.experimental.DoubleParam
|
||||
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
import org.chipsalliance.cde.config.{Field, Parameters, Config}
|
||||
import freechips.rocketchip.util.{ResetCatchAndSync}
|
||||
import freechips.rocketchip.prci._
|
||||
|
||||
import chipyard.harness.{ApplyHarnessBinders, HarnessBinders, HarnessClockInstantiatorKey}
|
||||
import chipyard.iobinders.HasIOBinders
|
||||
import chipyard.clocking.{SimplePllConfiguration, ClockDividerN}
|
||||
|
||||
|
||||
// HarnessClockInstantiators are classes which generate clocks that drive
|
||||
// TestHarness simulation models and any Clock inputs to the ChipTop
|
||||
trait HarnessClockInstantiator {
|
||||
val clockMap: LinkedHashMap[String, (Double, Clock)] = LinkedHashMap.empty
|
||||
|
||||
// request a clock at a particular frequency
|
||||
def requestClockHz(name: String, freqHzRequested: Double): Clock = {
|
||||
if (clockMap.contains(name)) {
|
||||
require(freqHzRequested == clockMap(name)._1,
|
||||
s"Request clock freq = $freqHzRequested != previously requested ${clockMap(name)._2} for requested clock $name")
|
||||
clockMap(name)._2
|
||||
} else {
|
||||
val clock = Wire(Clock())
|
||||
clockMap(name) = (freqHzRequested, clock)
|
||||
clock
|
||||
}
|
||||
}
|
||||
def requestClockMHz(name: String, freqMHzRequested: Double): Clock = {
|
||||
requestClockHz(name, freqMHzRequested * (1000 * 1000))
|
||||
}
|
||||
// refClock is the clock generated by TestDriver that is
|
||||
// passed to the TestHarness as its implicit clock
|
||||
def instantiateHarnessClocks(refClock: Clock, refClockFreqMHz: Double): Unit
|
||||
}
|
||||
|
||||
class ClockSourceAtFreqMHz(val freqMHz: Double) extends BlackBox(Map(
|
||||
"PERIOD" -> DoubleParam(1000/freqMHz)
|
||||
)) with HasBlackBoxInline {
|
||||
val io = IO(new ClockSourceIO)
|
||||
val moduleName = this.getClass.getSimpleName
|
||||
|
||||
setInline(s"$moduleName.v",
|
||||
s"""
|
||||
|module $moduleName #(parameter PERIOD="") (
|
||||
| input power,
|
||||
| input gate,
|
||||
| output clk);
|
||||
| timeunit 1ns/1ps;
|
||||
| reg clk_i = 1'b0;
|
||||
| always #(PERIOD/2.0) clk_i = ~clk_i & (power & ~gate);
|
||||
| assign clk = clk_i;
|
||||
|endmodule
|
||||
|""".stripMargin)
|
||||
}
|
||||
|
||||
|
||||
// The AbsoluteFreqHarnessClockInstantiator uses a Verilog blackbox to
|
||||
// provide the precise requested frequency.
|
||||
// This ClockInstantiator cannot be synthesized or run in FireSim
|
||||
// It is useful for RTL simulations
|
||||
class AbsoluteFreqHarnessClockInstantiator extends HarnessClockInstantiator {
|
||||
def instantiateHarnessClocks(refClock: Clock, refClockFreqMHz: Double): Unit = {
|
||||
// connect wires to clock source
|
||||
for ((name, (freqHz, clock)) <- clockMap) {
|
||||
val source = Module(new ClockSourceAtFreqMHz(freqHz / (1000 * 1000)))
|
||||
source.io.power := true.B
|
||||
source.io.gate := false.B
|
||||
|
||||
clock := source.io.clk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WithAbsoluteFreqHarnessClockInstantiator extends Config((site, here, up) => {
|
||||
case HarnessClockInstantiatorKey => () => new AbsoluteFreqHarnessClockInstantiator
|
||||
})
|
||||
|
||||
class AllClocksFromHarnessClockInstantiator extends HarnessClockInstantiator {
|
||||
def instantiateHarnessClocks(refClock: Clock, refClockFreqMHz: Double): Unit = {
|
||||
val freqs = clockMap.map(_._2._1)
|
||||
freqs.tail.foreach(t => require(t == freqs.head, s"Mismatching clocks $t != ${freqs.head}"))
|
||||
for ((name, (freq, clock)) <- clockMap) {
|
||||
val freqMHz = freq / (1000 * 1000)
|
||||
require(freqMHz == refClockFreqMHz,
|
||||
s"AllClocksFromHarnessClockInstantiator has reference ${refClockFreqMHz.toInt} MHz attempting to drive clock $name which requires $freqMHz MHz")
|
||||
|
||||
clock := refClock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WithAllClocksFromHarnessClockInstantiator extends Config((site, here, up) => {
|
||||
case HarnessClockInstantiatorKey => () => new AllClocksFromHarnessClockInstantiator
|
||||
})
|
||||
@@ -0,0 +1,101 @@
|
||||
package chipyard.harness
|
||||
|
||||
import chisel3._
|
||||
|
||||
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
import org.chipsalliance.cde.config.{Field, Parameters, Config}
|
||||
import freechips.rocketchip.util.{ResetCatchAndSync}
|
||||
import freechips.rocketchip.prci.{ClockBundle, ClockBundleParameters, ClockSinkParameters, ClockParameters}
|
||||
import freechips.rocketchip.stage.phases.TargetDirKey
|
||||
|
||||
import chipyard.harness.{ApplyHarnessBinders, HarnessBinders}
|
||||
import chipyard.iobinders.HasIOBinders
|
||||
import chipyard.clocking.{SimplePllConfiguration, ClockDividerN}
|
||||
import chipyard.{ChipTop}
|
||||
|
||||
// -------------------------------
|
||||
// Chipyard Test Harness
|
||||
// -------------------------------
|
||||
|
||||
case object MultiChipNChips extends Field[Option[Int]](None) // None means ignore MultiChipParams
|
||||
case class MultiChipParameters(chipId: Int) extends Field[Parameters]
|
||||
case object BuildTop extends Field[Parameters => LazyModule]((p: Parameters) => new ChipTop()(p))
|
||||
case object HarnessClockInstantiatorKey extends Field[() => HarnessClockInstantiator]()
|
||||
case object HarnessBinderClockFrequencyKey extends Field[Double](100.0) // MHz
|
||||
case object MultiChipIdx extends Field[Int](0)
|
||||
|
||||
class WithMultiChip(id: Int, p: Parameters) extends Config((site, here, up) => {
|
||||
case MultiChipParameters(`id`) => p
|
||||
case MultiChipNChips => Some(up(MultiChipNChips).getOrElse(0) max (id + 1))
|
||||
})
|
||||
|
||||
class WithHomogeneousMultiChip(n: Int, p: Parameters, idStart: Int = 0) extends Config((site, here, up) => {
|
||||
case MultiChipParameters(id) => if (id >= idStart && id < idStart + n) p else up(MultiChipParameters(id))
|
||||
case MultiChipNChips => Some(up(MultiChipNChips).getOrElse(0) max (idStart + n))
|
||||
})
|
||||
|
||||
class WithHarnessBinderClockFreqMHz(freqMHz: Double) extends Config((site, here, up) => {
|
||||
case HarnessBinderClockFrequencyKey => freqMHz
|
||||
})
|
||||
|
||||
// A TestHarness mixing this in will
|
||||
// - use the HarnessClockInstantiator clock provide
|
||||
trait HasHarnessInstantiators {
|
||||
implicit val p: Parameters
|
||||
// clock/reset of the chiptop reference clock (can be different than the implicit harness clock/reset)
|
||||
private val harnessBinderClockFreq: Double = p(HarnessBinderClockFrequencyKey)
|
||||
def getHarnessBinderClockFreqHz: Double = harnessBinderClockFreq * 1000000
|
||||
def getHarnessBinderClockFreqMHz: Double = harnessBinderClockFreq
|
||||
|
||||
// buildtopClock takes the refClockFreq, and drives the harnessbinders
|
||||
val harnessBinderClock = Wire(Clock())
|
||||
val harnessBinderReset = Wire(Reset())
|
||||
|
||||
// classes which inherit this trait should provide the below definitions
|
||||
def referenceClockFreqMHz: Double
|
||||
def referenceClock: Clock
|
||||
def referenceReset: Reset
|
||||
def success: Bool
|
||||
|
||||
// This can be accessed to get new clocks from the harness
|
||||
val harnessClockInstantiator = p(HarnessClockInstantiatorKey)()
|
||||
|
||||
val supportsMultiChip: Boolean = false
|
||||
|
||||
private val chipParameters = p(MultiChipNChips) match {
|
||||
case Some(n) => (0 until n).map { i => p(MultiChipParameters(i)).alterPartial {
|
||||
case TargetDirKey => p(TargetDirKey) // hacky fix
|
||||
case MultiChipIdx => i
|
||||
}}
|
||||
case None => Seq(p)
|
||||
}
|
||||
|
||||
// This shold be called last to build the ChipTops
|
||||
def instantiateChipTops(): Seq[LazyModule] = {
|
||||
require(p(MultiChipNChips).isEmpty || supportsMultiChip,
|
||||
s"Selected Harness does not support multi-chip")
|
||||
|
||||
val lazyDuts = chipParameters.zipWithIndex.map { case (q,i) =>
|
||||
LazyModule(q(BuildTop)(q)).suggestName(s"chiptop$i")
|
||||
}
|
||||
val duts = lazyDuts.map(l => Module(l.module))
|
||||
|
||||
withClockAndReset (harnessBinderClock, harnessBinderReset) {
|
||||
lazyDuts.zipWithIndex.foreach {
|
||||
case (d: HasIOBinders, i: Int) => ApplyHarnessBinders(this, d.lazySystem, d.portMap)(chipParameters(i))
|
||||
case _ =>
|
||||
}
|
||||
ApplyMultiHarnessBinders(this, lazyDuts)
|
||||
}
|
||||
|
||||
val harnessBinderClk = harnessClockInstantiator.requestClockMHz("harnessbinder_clock", getHarnessBinderClockFreqMHz)
|
||||
println(s"Harness binder clock is $harnessBinderClockFreq")
|
||||
harnessBinderClock := harnessBinderClk
|
||||
harnessBinderReset := ResetCatchAndSync(harnessBinderClk, referenceReset.asBool)
|
||||
|
||||
harnessClockInstantiator.instantiateHarnessClocks(referenceClock, referenceClockFreqMHz)
|
||||
|
||||
lazyDuts
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package chipyard.harness
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
|
||||
import org.chipsalliance.cde.config.{Field, Config, Parameters}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImpLike}
|
||||
import freechips.rocketchip.devices.debug._
|
||||
import freechips.rocketchip.subsystem._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
import testchipip._
|
||||
|
||||
import chipyard._
|
||||
import chipyard.clocking.{HasChipyardPRCI, ClockWithFreq}
|
||||
import chipyard.iobinders.{GetSystemParameters, JTAGChipIO, HasIOBinders}
|
||||
|
||||
import scala.reflect.{ClassTag}
|
||||
|
||||
case class MultiHarnessBinders(c0: Int, c1: Int) extends Field[MultiHarnessBinderMap](MultiHarnessBinderMapDefault)
|
||||
|
||||
class MultiHarnessBinder[T0, T1, S <: HasHarnessInstantiators, U0 <: Data, U1 <: Data]
|
||||
(chip0: Int, chip1: Int, fn: => (T0, T1, S, Seq[U0], Seq[U1]) => Unit)
|
||||
(implicit tag0: ClassTag[T0], tag1: ClassTag[T1], thtag: ClassTag[S], ptag0: ClassTag[U0], ptag1: ClassTag[U1])
|
||||
extends Config((site, here, up) => {
|
||||
// Override any HarnessBinders for chip0/chip1
|
||||
case MultiChipParameters(`chip0`) => new Config(
|
||||
new OverrideHarnessBinder[T0, S, U0]((system: T0, th: S, ports: Seq[U0]) => Nil) ++
|
||||
up(MultiChipParameters(chip0))
|
||||
)
|
||||
case MultiChipParameters(`chip1`) => new Config(
|
||||
new OverrideHarnessBinder[T1, S, U1]((system: T1, th: S, ports: Seq[U1]) => Nil) ++
|
||||
up(MultiChipParameters(chip1))
|
||||
)
|
||||
// Set the multiharnessbinder key
|
||||
case MultiHarnessBinders(`chip0`, `chip1`) => up(MultiHarnessBinders(chip0, chip1)) +
|
||||
((tag0.runtimeClass.toString, tag1.runtimeClass.toString) ->
|
||||
((c0: Any, c1: Any, th: HasHarnessInstantiators, ports0: Seq[Data], ports1: Seq[Data]) => {
|
||||
val pts0 = ports0.map(_.asInstanceOf[U0])
|
||||
val pts1 = ports1.map(_.asInstanceOf[U1])
|
||||
require(pts0.size == pts1.size)
|
||||
(c0, c1, th) match {
|
||||
case (c0: T0, c1: T1, th: S) => fn(c0, c1, th, pts0, pts1)
|
||||
case _ =>
|
||||
}
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
object ApplyMultiHarnessBinders {
|
||||
def apply(th: HasHarnessInstantiators, chips: Seq[LazyModule])(implicit p: Parameters): Unit = {
|
||||
Seq.tabulate(chips.size, chips.size) { case (i, j) => if (i != j) {
|
||||
(chips(i), chips(j)) match {
|
||||
case (l0: HasIOBinders, l1: HasIOBinders) => p(MultiHarnessBinders(i, j)).foreach {
|
||||
case ((s0, s1), f) => {
|
||||
f(l0.lazySystem , l1.lazySystem , th, l0.portMap(s0), l1.portMap(s1))
|
||||
f(l0.lazySystem.module, l1.lazySystem.module, th, l0.portMap(s0), l1.portMap(s1))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
class WithMultiChipSerialTL(chip0: Int, chip1: Int) extends MultiHarnessBinder(chip0, chip1, (
|
||||
(system0: CanHavePeripheryTLSerial, system1: CanHavePeripheryTLSerial,
|
||||
th: HasHarnessInstantiators,
|
||||
ports0: Seq[ClockedIO[SerialIO]], ports1: Seq[ClockedIO[SerialIO]]
|
||||
) => {
|
||||
require(ports0.size == ports1.size)
|
||||
(ports0 zip ports1).map { case (l, r) =>
|
||||
l.clock <> r.clock
|
||||
require(l.bits.w == r.bits.w)
|
||||
l.bits.flipConnect(r.bits)
|
||||
}
|
||||
}
|
||||
))
|
||||
38
generators/chipyard/src/main/scala/harness/TestHarness.scala
Normal file
38
generators/chipyard/src/main/scala/harness/TestHarness.scala
Normal file
@@ -0,0 +1,38 @@
|
||||
package chipyard.harness
|
||||
|
||||
import chisel3._
|
||||
|
||||
import scala.collection.mutable.{ArrayBuffer, LinkedHashMap}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
import org.chipsalliance.cde.config.{Field, Parameters}
|
||||
import freechips.rocketchip.util.{ResetCatchAndSync}
|
||||
import freechips.rocketchip.prci.{ClockBundle, ClockBundleParameters, ClockSinkParameters, ClockParameters}
|
||||
|
||||
import chipyard.harness.{ApplyHarnessBinders, HarnessBinders}
|
||||
import chipyard.iobinders.HasIOBinders
|
||||
import chipyard.clocking.{SimplePllConfiguration, ClockDividerN}
|
||||
import chipyard.{ChipTop}
|
||||
|
||||
// -------------------------------
|
||||
// Chipyard Test Harness
|
||||
// -------------------------------
|
||||
|
||||
class TestHarness(implicit val p: Parameters) extends Module with HasHarnessInstantiators {
|
||||
val io = IO(new Bundle {
|
||||
val success = Output(Bool())
|
||||
})
|
||||
val success = WireInit(false.B)
|
||||
io.success := success
|
||||
|
||||
override val supportsMultiChip = true
|
||||
|
||||
// By default, the chipyard makefile sets the TestHarness implicit clock to be 1GHz
|
||||
// This clock shouldn't be used by this TestHarness however, as most users
|
||||
// will use the AbsoluteFreqHarnessClockInstantiator, which generates clocks
|
||||
// in verilog blackboxes
|
||||
def referenceClockFreqMHz = 1000.0
|
||||
def referenceClock = clock
|
||||
def referenceReset = reset
|
||||
|
||||
val lazyDuts = instantiateChipTops()
|
||||
}
|
||||
17
generators/chipyard/src/main/scala/harness/package.scala
Normal file
17
generators/chipyard/src/main/scala/harness/package.scala
Normal file
@@ -0,0 +1,17 @@
|
||||
package chipyard
|
||||
|
||||
import chisel3._
|
||||
import scala.collection.immutable.ListMap
|
||||
|
||||
package object harness
|
||||
{
|
||||
type HarnessBinderFunction = (Any, HasHarnessInstantiators, Seq[Data]) => Unit
|
||||
type HarnessBinderMap = Map[String, HarnessBinderFunction]
|
||||
def HarnessBinderMapDefault: HarnessBinderMap = (new ListMap[String, HarnessBinderFunction])
|
||||
.withDefaultValue((t: Any, th: HasHarnessInstantiators, d: Seq[Data]) => ())
|
||||
|
||||
type MultiHarnessBinderFunction = (Any, Any, HasHarnessInstantiators, Seq[Data], Seq[Data]) => Unit
|
||||
type MultiHarnessBinderMap = Map[(String, String), MultiHarnessBinderFunction]
|
||||
def MultiHarnessBinderMapDefault: MultiHarnessBinderMap = (new ListMap[(String, String), MultiHarnessBinderFunction])
|
||||
.withDefaultValue((_: Any, _: Any, _: HasHarnessInstantiators, _: Seq[Data], _: Seq[Data]) => ())
|
||||
}
|
||||
90
generators/chipyard/src/main/scala/upf/ChipTopUPF.scala
Normal file
90
generators/chipyard/src/main/scala/upf/ChipTopUPF.scala
Normal file
@@ -0,0 +1,90 @@
|
||||
// See LICENSE for license details
|
||||
package chipyard.upf
|
||||
|
||||
import scala.collection.mutable.{ListBuffer}
|
||||
import scalax.collection.mutable.{Graph}
|
||||
import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._
|
||||
|
||||
import chipyard.harness.{TestHarness}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
|
||||
object ChipTopUPF {
|
||||
|
||||
def default: UPFFunc.UPFFunction = {
|
||||
case top: LazyModule => {
|
||||
val modulesList = getLazyModules(top)
|
||||
val pdList = createPowerDomains(modulesList)
|
||||
val g = connectPDHierarchy(pdList)
|
||||
traverseGraph(g, UPFGenerator.generateUPF)
|
||||
}
|
||||
}
|
||||
|
||||
def getLazyModules(top: LazyModule): ListBuffer[LazyModule] = {
|
||||
var i = 0
|
||||
var result = new ListBuffer[LazyModule]()
|
||||
result.append(top)
|
||||
while (i < result.length) {
|
||||
val lazyMod = result(i)
|
||||
for (child <- lazyMod.getChildren) {
|
||||
result.append(child)
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
def createPowerDomains(modulesList: ListBuffer[LazyModule]): ListBuffer[PowerDomain] = {
|
||||
var pdList = ListBuffer[PowerDomain]()
|
||||
for (pdInput <- UPFInputs.upfInfo) {
|
||||
val pd = new PowerDomain(name=pdInput.name, modules=getPDModules(pdInput, modulesList),
|
||||
isTop=pdInput.isTop, isGated=pdInput.isGated,
|
||||
highVoltage=pdInput.highVoltage, lowVoltage=pdInput.lowVoltage)
|
||||
pdList.append(pd)
|
||||
}
|
||||
return pdList
|
||||
}
|
||||
|
||||
def getPDModules(pdInput: PowerDomainInput, modulesList: ListBuffer[LazyModule]): ListBuffer[LazyModule] = {
|
||||
var pdModules = ListBuffer[LazyModule]()
|
||||
for (moduleName <- pdInput.moduleList) {
|
||||
var module = modulesList.filter(_.module.name == moduleName)
|
||||
if (module.length == 1) { // filter returns a collection
|
||||
pdModules.append(module(0))
|
||||
} else {
|
||||
module = modulesList.filter(_.module.instanceName == moduleName)
|
||||
if (module.length == 1) {
|
||||
pdModules.append(module(0))
|
||||
} else {
|
||||
module = modulesList.filter(_.module.pathName == moduleName)
|
||||
if (module.length == 1) {
|
||||
pdModules.append(module(0))
|
||||
} else {
|
||||
throw new Exception(s"PowerDomainInput module list doesn't exist in design.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pdModules
|
||||
}
|
||||
|
||||
def connectPDHierarchy(pdList: ListBuffer[PowerDomain]): Graph[PowerDomain, DiEdge] = {
|
||||
var g = Graph[PowerDomain, DiEdge]()
|
||||
for (pd <- pdList) {
|
||||
val pdInput = UPFInputs.upfInfo.filter(_.name == pd.name)(0)
|
||||
val childPDs = pdList.filter(x => pdInput.childrenPDs.contains(x.name))
|
||||
for (childPD <- childPDs) {
|
||||
g += (pd ~> childPD) // directed edge from pd to childPD
|
||||
}
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
def traverseGraph(g: Graph[PowerDomain, DiEdge], action: (PowerDomain, Graph[PowerDomain, DiEdge]) => Unit): Unit = {
|
||||
for (node <- g.nodes.filter(_.diPredecessors.isEmpty)) { // all nodes without parents
|
||||
g.outerNodeTraverser(node).foreach(pd => action(pd, g))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case object ChipTopUPFAspect extends UPFAspect[chipyard.harness.TestHarness](ChipTopUPF.default)
|
||||
24
generators/chipyard/src/main/scala/upf/UPFAspect.scala
Normal file
24
generators/chipyard/src/main/scala/upf/UPFAspect.scala
Normal file
@@ -0,0 +1,24 @@
|
||||
// See LICENSE for license details
|
||||
package chipyard.upf
|
||||
|
||||
import chisel3.aop.{Aspect}
|
||||
import firrtl.{AnnotationSeq}
|
||||
import chipyard.harness.{TestHarness}
|
||||
import freechips.rocketchip.stage.phases.{TargetDirKey}
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
|
||||
abstract class UPFAspect[T <: TestHarness](upf: UPFFunc.UPFFunction) extends Aspect[T] {
|
||||
|
||||
final override def toAnnotation(top: T): AnnotationSeq = {
|
||||
UPFFunc.UPFPath = top.p(TargetDirKey) + "/upf"
|
||||
require(top.lazyDuts.length == 1) // currently only supports 1 chiptop
|
||||
upf(top.lazyDuts.head)
|
||||
AnnotationSeq(Seq()) // noop
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object UPFFunc {
|
||||
type UPFFunction = PartialFunction[LazyModule, Unit]
|
||||
var UPFPath = "" // output dir path
|
||||
}
|
||||
264
generators/chipyard/src/main/scala/upf/UPFGen.scala
Normal file
264
generators/chipyard/src/main/scala/upf/UPFGen.scala
Normal file
@@ -0,0 +1,264 @@
|
||||
// See LICENSE for license details
|
||||
package chipyard.upf
|
||||
|
||||
import java.io.{FileWriter}
|
||||
import java.nio.file.{Paths, Files}
|
||||
import scala.collection.mutable.{ListBuffer}
|
||||
import scalax.collection.mutable.{Graph}
|
||||
import scalax.collection.GraphPredef._, scalax.collection.GraphEdge._
|
||||
|
||||
import freechips.rocketchip.diplomacy.{LazyModule}
|
||||
|
||||
case class PowerDomain (val name: String, val modules: ListBuffer[LazyModule],
|
||||
val isTop: Boolean, val isGated: Boolean,
|
||||
val highVoltage: Double, val lowVoltage: Double) {
|
||||
val mainVoltage = isGated match {
|
||||
case true => highVoltage // gated nets should have access to high voltage rail (since they are being gated to optimize power)
|
||||
case false => lowVoltage // currently assuming non-gated nets are on low voltage rail
|
||||
}
|
||||
}
|
||||
|
||||
object UPFGenerator {
|
||||
|
||||
def generateUPF(pd: PowerDomain, g: Graph[PowerDomain, DiEdge]): Unit = {
|
||||
val node = g.get(pd)
|
||||
val children = node.diSuccessors.map(x => x.toOuter).toList
|
||||
val pdList = g.nodes.map(x => x.toOuter).toList
|
||||
val filePath = UPFFunc.UPFPath
|
||||
val fileName = s"${pd.name}.upf"
|
||||
writeFile(filePath, fileName, createMessage(pd, children, pdList))
|
||||
}
|
||||
|
||||
def createMessage(pd: PowerDomain, children: List[PowerDomain], pdList: List[PowerDomain]): String = {
|
||||
var message = ""
|
||||
message += loadUPF(pd, children)
|
||||
message += createPowerDomains(pd)
|
||||
message += createSupplyPorts(pd)
|
||||
message += createSupplyNets(pd)
|
||||
message += connectSupplies(pd)
|
||||
message += setDomainNets(pd)
|
||||
message += createPowerSwitches(pd)
|
||||
message += createPowerStateTable(pd, getPorts(pd, children))
|
||||
message += createLevelShifters(pd, pdList)
|
||||
return message
|
||||
}
|
||||
|
||||
def writeFile(filePath: String, fileName: String, message: String): Unit = {
|
||||
if (!Files.exists(Paths.get(filePath))) {
|
||||
Files.createDirectories(Paths.get(filePath))
|
||||
}
|
||||
val fw = new FileWriter(s"${filePath}/${fileName}", false)
|
||||
fw.write(message)
|
||||
fw.close()
|
||||
}
|
||||
|
||||
def getPorts(pd: PowerDomain, children: List[PowerDomain]): ListBuffer[String] = {
|
||||
var portsList = ListBuffer[String]()
|
||||
portsList += "VDDH"
|
||||
portsList += "VDDL"
|
||||
if (pd.isGated) {
|
||||
portsList += s"VDD_${pd.name}"
|
||||
}
|
||||
for (child <- children) {
|
||||
if (child.isGated) {
|
||||
portsList += s"VDD_${child.name}"
|
||||
}
|
||||
}
|
||||
return portsList
|
||||
}
|
||||
|
||||
def loadUPF(pd: PowerDomain, children: List[PowerDomain]): String = {
|
||||
var message = "##### Set Scope and Load UPF #####\n"
|
||||
var subMessage = s"set_scope /${pd.modules(0).module.name}\n" //
|
||||
children.foreach{
|
||||
child => {
|
||||
subMessage += s"load_upf ${child.name}.upf -scope ${child.modules(0).module.name}\n"
|
||||
}
|
||||
}
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def createPowerDomains(pd: PowerDomain): String = {
|
||||
var message = "##### Create Power Domains #####\n"
|
||||
var subMessage = ""
|
||||
pd.isTop match {
|
||||
case true => subMessage += s"create_power_domain ${pd.name} -include_scope\n"
|
||||
case false => {
|
||||
subMessage += s"create_power_domain ${pd.name} -elements { "
|
||||
for (module <- pd.modules) {
|
||||
subMessage += s"${module.module.name} "
|
||||
}
|
||||
subMessage += "}\n"
|
||||
}
|
||||
}
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def createSupplyPorts(pd: PowerDomain): String = {
|
||||
if (!pd.isTop) {
|
||||
return ""
|
||||
}
|
||||
var message = "##### Create Supply Ports #####\n"
|
||||
var subMessage = pd.isTop match {
|
||||
case true => {
|
||||
s"create_supply_port VDDH -direction in -domain ${pd.name}\n" +
|
||||
s"create_supply_port VDDL -direction in -domain ${pd.name}\n" +
|
||||
s"create_supply_port VSS -direction in -domain ${pd.name}\n"
|
||||
}
|
||||
case false => ""
|
||||
}
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def createSupplyNets(pd: PowerDomain): String = {
|
||||
var message = "##### Create Supply Nets #####\n"
|
||||
var subMessage = pd.isTop match {
|
||||
case true => {
|
||||
s"create_supply_net VDDH -domain ${pd.name}\n" +
|
||||
s"create_supply_net VDDL -domain ${pd.name}\n" +
|
||||
s"create_supply_net VSS -domain ${pd.name}\n"
|
||||
}
|
||||
case false => {
|
||||
s"create_supply_net VDDH -domain ${pd.name} -reuse\n" +
|
||||
s"create_supply_net VDDL -domain ${pd.name} -reuse\n" +
|
||||
s"create_supply_net VSS -domain ${pd.name} -reuse\n"
|
||||
}
|
||||
}
|
||||
if (pd.isGated) {
|
||||
subMessage += s"create_supply_net VDD_${pd.name} -domain ${pd.name}\n"
|
||||
}
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def connectSupplies(pd: PowerDomain): String = {
|
||||
var message = "##### Connect Supply Nets and Ports #####\n"
|
||||
var subMessage = "connect_supply_net VDDH -ports VDDH\n" +
|
||||
"connect_supply_net VDDL -ports VDDL\n" +
|
||||
"connect_supply_net VSS -ports VSS\n"
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def setDomainNets(pd: PowerDomain): String = {
|
||||
var message = "##### Set Domain Supply Nets #####\n"
|
||||
var subMessage = pd.isGated match {
|
||||
case true => s"set_domain_supply_net ${pd.name} -primary_power_net VDD_${pd.name} -primary_ground_net VSS\n"
|
||||
case false => s"set_domain_supply_net ${pd.name} -primary_power_net VDDL -primary_ground_net VSS\n"
|
||||
}
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def createPowerSwitches(pd: PowerDomain): String = {
|
||||
if (!pd.isGated) {
|
||||
return ""
|
||||
}
|
||||
var message = "##### Power Switches #####\n"
|
||||
var subMessage = pd.isGated match {
|
||||
case true => s"""create_power_switch sw_${pd.name} -domain ${pd.name} -input_supply_port "psw_VDDH VDDH" """ +
|
||||
s"""-output_supply_port "psw_VDD_${pd.name} VDD_${pd.name}" """ +
|
||||
s"""-control_port "psw_${pd.name}_en ${pd.modules(0).module.name}/${pd.modules(0).module.name}_en" """ +
|
||||
s"""-on_state "psw_${pd.name}_ON psw_VDDH { !psw_${pd.name}_en }"""" + "\n"
|
||||
case false => ""
|
||||
}
|
||||
message += subMessage
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def createPowerStateTable(pd: PowerDomain, portsList: ListBuffer[String]): String = {
|
||||
if (!pd.isTop) {
|
||||
return ""
|
||||
}
|
||||
var message = "##### Power State Table #####\n"
|
||||
var portStates = ""
|
||||
var createPST = "create_pst pst_table -supplies { "
|
||||
|
||||
for (port <- portsList) {
|
||||
createPST += s"${port} "
|
||||
if (port == "VDDH") {
|
||||
portStates += s"add_port_state ${port} -state { HighVoltage ${pd.highVoltage} }\n"
|
||||
} else if (port == "VDDL") {
|
||||
portStates += s"add_port_state ${port} -state { LowVoltage ${pd.lowVoltage} }\n"
|
||||
} else { // gated
|
||||
portStates += s"add_port_state ${port} -state { HighVoltage ${pd.highVoltage } -state { ${port}_OFF off }\n"
|
||||
}
|
||||
}
|
||||
portStates += "\n"
|
||||
createPST += "}\n\n"
|
||||
|
||||
var pstStates = ""
|
||||
for (state <- UPFInputs.states.keys) {
|
||||
val stateVal = getStateVal(pd, state)
|
||||
pstStates += s"add_pst_state ${state} -pst pst_table -state { "
|
||||
for (port <- portsList) {
|
||||
if (port == "VDDH") {
|
||||
pstStates += s"HighVoltage "
|
||||
} else if (port == "VDDL") {
|
||||
pstStates += s"LowVoltage "
|
||||
} else { // gated
|
||||
stateVal match {
|
||||
case 0 => pstStates += s"${port}_OFF "
|
||||
case 1 => pstStates += s"HighVoltage "
|
||||
}
|
||||
}
|
||||
}
|
||||
pstStates += "}\n"
|
||||
}
|
||||
message += portStates
|
||||
message += createPST
|
||||
message += pstStates
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
def getStateVal(pd: PowerDomain, state: String): Int = {
|
||||
val stateVals = UPFInputs.states(state).split(",").map(_.trim.toInt)
|
||||
val index = UPFInputs.domains.indexOf(pd.name)
|
||||
return stateVals(index)
|
||||
}
|
||||
|
||||
// current strategy: for each power domain, create level shifters for outputs going to all other pds
|
||||
// not creating level shifters for inputs since every pd will already shift its outputs
|
||||
// creating level shifters going to every other pd since not sure how to check if there is communication or not between any 2
|
||||
def createLevelShifters(pd: PowerDomain, pdList: List[PowerDomain]): String = {
|
||||
var message = "##### Level Shifters #####\n"
|
||||
for (pd2 <- pdList) {
|
||||
if (pd != pd2) {
|
||||
val voltage1 = pd.mainVoltage
|
||||
val voltage2 = pd2.mainVoltage
|
||||
var subMessage = voltage1 match {
|
||||
case x if x < voltage2 => {
|
||||
s"set_level_shifter LtoH_${pd.name}_to_${pd2.name} " +
|
||||
s"-domain ${pd.name} " +
|
||||
"-applies_to outputs " +
|
||||
"rule low_to_high " +
|
||||
"-location self\n"
|
||||
}
|
||||
case y if y > voltage2 => {
|
||||
s"set_level_shifter HtoL_${pd.name}_to_${pd2.name} " +
|
||||
s"-domain ${pd.name} " +
|
||||
"-applies_to outputs " +
|
||||
"rule high_to_low " +
|
||||
"-location self\n"
|
||||
}
|
||||
case _ => ""
|
||||
}
|
||||
message += subMessage
|
||||
}
|
||||
}
|
||||
message += "\n"
|
||||
return message
|
||||
}
|
||||
|
||||
}
|
||||
54
generators/chipyard/src/main/scala/upf/UPFInputs.scala
Normal file
54
generators/chipyard/src/main/scala/upf/UPFInputs.scala
Normal file
@@ -0,0 +1,54 @@
|
||||
// See LICENSE for license details
|
||||
package chipyard.upf
|
||||
|
||||
// outputs are dumped in vlsi/generated-src/upf
|
||||
object UPFInputs {
|
||||
|
||||
/**
|
||||
* UPF info
|
||||
* each PowerDomainInput represents a desired power domain
|
||||
* each input will contain all the necessary info to describe a power domain in UPF, including hierarchy
|
||||
*/
|
||||
val upfInfo = List(
|
||||
PowerDomainInput(name="PD_top", isTop=true, moduleList=List("DigitalTop"),
|
||||
parentPD="", childrenPDs=List("PD_RocketTile1", "PD_RocketTile2"),
|
||||
isGated=false, highVoltage=3.9, lowVoltage=3.4),
|
||||
PowerDomainInput(name="PD_RocketTile1", isTop=false, moduleList=List("tile_prci_domain"),
|
||||
parentPD="PD_top", childrenPDs=List(),
|
||||
isGated=false, highVoltage=3.9, lowVoltage=3.1),
|
||||
PowerDomainInput(name="PD_RocketTile2", isTop=false, moduleList=List("tile_prci_domain_1"),
|
||||
parentPD="PD_top", childrenPDs=List(),
|
||||
isGated=false, highVoltage=3.9, lowVoltage=3.2),
|
||||
)
|
||||
|
||||
|
||||
/**
|
||||
* PST info
|
||||
* experimental Power State Table input, used to gate power domains based on specified power states
|
||||
* place names of all power domains to be gated in the domains list
|
||||
* states will map different keywords (arbitrary strings) to a binary on or off (1 or 0) to form a power state
|
||||
* order of domains in list corresponds to order of values in each states mapping
|
||||
*/
|
||||
val domains = List("PD_top", "PD_RocketTile1", "PD_RocketTile2")
|
||||
val states = Map(
|
||||
"ON" -> "1, 1, 1",
|
||||
"OFF" -> "0, 0, 0"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of a power domain used to generate UPF.
|
||||
*
|
||||
* @param name name of the power domain.
|
||||
* @param isTop if the power domain is the top level or not.
|
||||
* @param moduleList refers to all the Verilog modules belonging to this power domain. Can be module name, instance name, or full path name.
|
||||
* @param parentPD the name of the parent power domain to this one.
|
||||
* @param childrenPDs names of all the children power domains to this one.
|
||||
* @param isGated if the power domain is gated or not.
|
||||
* @param highVoltage voltage value of the high voltage rail (currently, gated nets have access to high voltage since they are optimized to save power).
|
||||
* @param lowVoltage voltage value of the low voltage rail (currently, non-gated nets default to the low voltage rail).
|
||||
*/
|
||||
case class PowerDomainInput(name: String, isTop: Boolean, moduleList: List[String],
|
||||
parentPD: String, childrenPDs: List[String],
|
||||
isGated: Boolean, highVoltage: Double, lowVoltage: Double)
|
||||
Submodule generators/constellation updated: e9f1c828ca...8184e0e7e3
Submodule generators/cva6 updated: 0011494bb7...46323fcd74
Submodule generators/fft-generator updated: be8ab768bd...f598d0c359
@@ -4,6 +4,7 @@ package firesim.firesim
|
||||
|
||||
import chisel3._
|
||||
import chisel3.experimental.annotate
|
||||
import chisel3.experimental.{DataMirror, Direction}
|
||||
import chisel3.util.experimental.BoringUtils
|
||||
|
||||
import org.chipsalliance.cde.config.{Field, Config, Parameters}
|
||||
@@ -30,12 +31,12 @@ import cva6.CVA6Tile
|
||||
import boom.common.{BoomTile}
|
||||
import barstools.iocell.chisel._
|
||||
import chipyard.iobinders.{IOBinders, OverrideIOBinder, ComposeIOBinder, GetSystemParameters, IOCellKey}
|
||||
import chipyard.{HasHarnessSignalReferences}
|
||||
import chipyard._
|
||||
import chipyard.harness._
|
||||
|
||||
object MainMemoryConsts {
|
||||
val regionNamePrefix = "MainMemory"
|
||||
def globalName = s"${regionNamePrefix}_${NodeIdx()}"
|
||||
def globalName()(implicit p: Parameters) = s"${regionNamePrefix}_${p(MultiChipIdx)}"
|
||||
}
|
||||
|
||||
trait Unsupported {
|
||||
@@ -68,15 +69,14 @@ class WithFireSimIOCellModels extends Config((site, here, up) => {
|
||||
case IOCellKey => FireSimIOCellParams()
|
||||
})
|
||||
|
||||
class WithSerialBridge extends OverrideHarnessBinder({
|
||||
class WithTSIBridgeAndHarnessRAMOverSerialTL extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryTLSerial, th: FireSim, ports: Seq[ClockedIO[SerialIO]]) => {
|
||||
ports.map { port =>
|
||||
implicit val p = GetSystemParameters(system)
|
||||
val bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
|
||||
val ram = withClockAndReset(th.buildtopClock, th.buildtopReset) {
|
||||
SerialAdapter.connectHarnessRAM(system.serdesser.get, bits, th.buildtopReset)
|
||||
}
|
||||
SerialBridge(th.buildtopClock, ram.module.io.tsi_ser, p(ExtMem).map(_ => MainMemoryConsts.globalName), th.buildtopReset.asBool)
|
||||
val bits = port.bits
|
||||
port.clock := th.harnessBinderClock
|
||||
val ram = TSIHarness.connectRAM(system.serdesser.get, bits, th.harnessBinderReset)
|
||||
TSIBridge(th.harnessBinderClock, ram.module.io.tsi, p(ExtMem).map(_ => MainMemoryConsts.globalName), th.harnessBinderReset.asBool)
|
||||
}
|
||||
Nil
|
||||
}
|
||||
@@ -97,13 +97,13 @@ class WithUARTBridge extends OverrideHarnessBinder({
|
||||
val pbusClockNode = system.outer.asInstanceOf[HasTileLinkLocations].locateTLBusWrapper(PBUS).fixedClockNode
|
||||
val pbusClock = pbusClockNode.in.head._1.clock
|
||||
BoringUtils.bore(pbusClock, Seq(uartSyncClock))
|
||||
ports.map { p => UARTBridge(uartSyncClock, p, th.buildtopReset.asBool)(system.p) }; Nil
|
||||
ports.map { p => UARTBridge(uartSyncClock, p, th.harnessBinderReset.asBool)(system.p) }; Nil
|
||||
})
|
||||
|
||||
class WithBlockDeviceBridge extends OverrideHarnessBinder({
|
||||
(system: CanHavePeripheryBlockDevice, th: FireSim, ports: Seq[ClockedIO[BlockDeviceIO]]) => {
|
||||
implicit val p: Parameters = GetSystemParameters(system)
|
||||
ports.map { b => BlockDevBridge(b.clock, b.bits, th.buildtopReset.asBool) }
|
||||
ports.map { b => BlockDevBridge(b.clock, b.bits, th.harnessBinderReset.asBool) }
|
||||
Nil
|
||||
}
|
||||
})
|
||||
@@ -113,31 +113,25 @@ class WithAXIOverSerialTLCombinedBridges extends OverrideHarnessBinder({
|
||||
implicit val p = GetSystemParameters(system)
|
||||
|
||||
p(SerialTLKey).map({ sVal =>
|
||||
require(sVal.axiMemOverSerialTLParams.isDefined)
|
||||
val axiDomainParams = sVal.axiMemOverSerialTLParams.get
|
||||
require(sVal.isMemoryDevice)
|
||||
|
||||
val serialTLManagerParams = sVal.serialTLManagerParams.get
|
||||
val axiDomainParams = serialTLManagerParams.axiMemOverSerialTLParams.get
|
||||
require(serialTLManagerParams.isMemoryDevice)
|
||||
val memFreq = axiDomainParams.getMemFrequency(system.asInstanceOf[HasTileLinkLocations])
|
||||
|
||||
ports.map({ port =>
|
||||
val axiClock = p(ClockBridgeInstantiatorKey).requestClock("mem_over_serial_tl_clock", memFreq)
|
||||
val axiClockBundle = Wire(new ClockBundle(ClockBundleParameters()))
|
||||
axiClockBundle.clock := axiClock
|
||||
axiClockBundle.reset := ResetCatchAndSync(axiClock, th.buildtopReset.asBool)
|
||||
val axiClock = th.harnessClockInstantiator.requestClockHz("mem_over_serial_tl_clock", memFreq)
|
||||
|
||||
val serial_bits = SerialAdapter.asyncQueue(port, th.buildtopClock, th.buildtopReset)
|
||||
|
||||
val harnessMultiClockAXIRAM = withClockAndReset(th.buildtopClock, th.buildtopReset) {
|
||||
SerialAdapter.connectHarnessMultiClockAXIRAM(
|
||||
system.serdesser.get,
|
||||
serial_bits,
|
||||
axiClockBundle,
|
||||
th.buildtopReset)
|
||||
}
|
||||
SerialBridge(th.buildtopClock, harnessMultiClockAXIRAM.module.io.tsi_ser, Some(MainMemoryConsts.globalName), th.buildtopReset.asBool)
|
||||
val serial_bits = port.bits
|
||||
port.clock := th.harnessBinderClock
|
||||
val harnessMultiClockAXIRAM = TSIHarness.connectMultiClockAXIRAM(
|
||||
system.serdesser.get,
|
||||
serial_bits,
|
||||
axiClock,
|
||||
ResetCatchAndSync(axiClock, th.harnessBinderReset.asBool))
|
||||
TSIBridge(th.harnessBinderClock, harnessMultiClockAXIRAM.module.io.tsi, Some(MainMemoryConsts.globalName), th.harnessBinderReset.asBool)
|
||||
|
||||
// connect SimAxiMem
|
||||
(harnessMultiClockAXIRAM.mem_axi4 zip harnessMultiClockAXIRAM.memNode.edges.in).map { case (axi4, edge) =>
|
||||
(harnessMultiClockAXIRAM.mem_axi4.get zip harnessMultiClockAXIRAM.memNode.get.edges.in).map { case (axi4, edge) =>
|
||||
val nastiKey = NastiParameters(axi4.bits.r.bits.data.getWidth,
|
||||
axi4.bits.ar.bits.addr.getWidth,
|
||||
axi4.bits.ar.bits.id.getWidth)
|
||||
@@ -192,7 +186,7 @@ class WithDromajoBridge extends ComposeHarnessBinder({
|
||||
|
||||
class WithTraceGenBridge extends OverrideHarnessBinder({
|
||||
(system: TraceGenSystemModuleImp, th: FireSim, ports: Seq[Bool]) =>
|
||||
ports.map { p => GroundTestBridge(th.buildtopClock, p)(system.p) }; Nil
|
||||
ports.map { p => GroundTestBridge(th.harnessBinderClock, p)(system.p) }; Nil
|
||||
})
|
||||
|
||||
class WithFireSimMultiCycleRegfile extends ComposeIOBinder({
|
||||
@@ -232,7 +226,7 @@ class WithFireSimFAME5 extends ComposeIOBinder({
|
||||
|
||||
// Shorthand to register all of the provided bridges above
|
||||
class WithDefaultFireSimBridges extends Config(
|
||||
new WithSerialBridge ++
|
||||
new WithTSIBridgeAndHarnessRAMOverSerialTL ++
|
||||
new WithNICBridge ++
|
||||
new WithUARTBridge ++
|
||||
new WithBlockDeviceBridge ++
|
||||
@@ -245,7 +239,7 @@ class WithDefaultFireSimBridges extends Config(
|
||||
|
||||
// Shorthand to register all of the provided mmio-only bridges above
|
||||
class WithDefaultMMIOOnlyFireSimBridges extends Config(
|
||||
new WithSerialBridge ++
|
||||
new WithTSIBridgeAndHarnessRAMOverSerialTL ++
|
||||
new WithUARTBridge ++
|
||||
new WithBlockDeviceBridge ++
|
||||
new WithFASEDBridge ++
|
||||
|
||||
@@ -20,258 +20,76 @@ import chipyard.harness._
|
||||
import chipyard.iobinders._
|
||||
import chipyard.clocking._
|
||||
|
||||
// Determines the number of times to instantiate the DUT in the harness.
|
||||
// Subsumes legacy supernode support
|
||||
case object NumNodes extends Field[Int](1)
|
||||
|
||||
class WithNumNodes(n: Int) extends Config((pname, site, here) => {
|
||||
case NumNodes => n
|
||||
})
|
||||
|
||||
// Hacky: Set before each node is generated. Ideally we'd give IO binders
|
||||
// accesses to the the Harness's parameters instance. We could then alter that.
|
||||
object NodeIdx {
|
||||
private var idx = 0
|
||||
def increment(): Unit = {idx = idx + 1 }
|
||||
def apply(): Int = idx
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specifies DUT clocks for the rational clock bridge
|
||||
*
|
||||
* @param allClocks Seq. of RationalClocks that want a clock
|
||||
*
|
||||
* @param baseClockName Name of domain that the allClocks is rational to
|
||||
*
|
||||
* @param baseFreqRequested Freq. for the reference domain in Hz
|
||||
*/
|
||||
case class BuildTopClockParameters(allClocks: Seq[RationalClock], baseClockName: String, baseFreqRequested: Double)
|
||||
|
||||
/**
|
||||
* Under FireSim's current multiclock implementation there can be only a
|
||||
* single clock bridge. This requires, therefore, that it be instantiated in
|
||||
* the harness and reused across all supernode instances. This class attempts to
|
||||
* memoize its instantiation such that it can be referenced from within a ClockScheme function.
|
||||
*/
|
||||
class ClockBridgeInstantiator {
|
||||
private val _harnessClockMap: LinkedHashMap[String, (Double, Clock)] = LinkedHashMap.empty
|
||||
class FireSimClockBridgeInstantiator extends HarnessClockInstantiator {
|
||||
// connect all clock wires specified to the RationalClockBridge
|
||||
def instantiateHarnessClocks(refClock: Clock, refClockFreqMHz: Double): Unit = {
|
||||
val sinks = clockMap.map({ case (name, (freq, bundle)) =>
|
||||
ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))), name=Some(name))
|
||||
}).toSeq
|
||||
|
||||
// Assumes that the supernode implementation results in duplicated clocks
|
||||
// (i.e. only 1 set of clocks is generated for all BuildTop designs)
|
||||
private var _buildTopClockParams: Option[BuildTopClockParameters] = None
|
||||
private val _buildTopClockMap: LinkedHashMap[String, (RationalClock, Clock)] = LinkedHashMap.empty
|
||||
private var _buildTopClockRecord: Option[RecordMap[Clock]] = None
|
||||
val pllConfig = new SimplePllConfiguration("firesimRationalClockBridge", sinks)
|
||||
pllConfig.emitSummaries()
|
||||
|
||||
/**
|
||||
* Request a clock at a particular frequency
|
||||
*
|
||||
* @param name An identifier for the associated clock domain
|
||||
*
|
||||
* @param freqRequested Freq. for the domain in Hz
|
||||
*/
|
||||
def requestClock(name: String, freqRequested: Double): Clock = {
|
||||
val clkWire = Wire(new Clock)
|
||||
_harnessClockMap(name) = (freqRequested, clkWire)
|
||||
clkWire
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a RecordMap of clocks for a set of input RationalClocks. Used to drive
|
||||
* the design elaborated by buildtop
|
||||
*
|
||||
* @param clockMapParameters Defines the set of required clocks
|
||||
*/
|
||||
def requestClockRecordMap(clockMapParameters: BuildTopClockParameters): RecordMap[Clock] = {
|
||||
if (_buildTopClockParams.isDefined) {
|
||||
require(_buildTopClockParams.get == clockMapParameters, "Must request same set of clocks on repeated invocations.")
|
||||
} else {
|
||||
val clockRecord = Wire(RecordMap(clockMapParameters.allClocks.map { c => (c.name, Clock()) }:_*))
|
||||
// Build up the mutable structures describing the clocks for the dut
|
||||
_buildTopClockParams = Some(clockMapParameters)
|
||||
_buildTopClockRecord = Some(clockRecord)
|
||||
|
||||
for (clock <- clockMapParameters.allClocks) {
|
||||
val clockWire = Wire(new Clock)
|
||||
_buildTopClockMap(clock.name) = (clock, clockWire)
|
||||
clockRecord(clock.name).get := clockWire
|
||||
var instantiatedClocks = LinkedHashMap[Int, (Clock, Seq[String])]()
|
||||
// connect wires to clock source
|
||||
def findOrInstantiate(freqMHz: Int, name: String): Clock = {
|
||||
if (!instantiatedClocks.contains(freqMHz)) {
|
||||
val clock = Wire(Clock())
|
||||
instantiatedClocks(freqMHz) = (clock, Seq(name))
|
||||
} else {
|
||||
instantiatedClocks(freqMHz) = (instantiatedClocks(freqMHz)._1, instantiatedClocks(freqMHz)._2 :+ name)
|
||||
}
|
||||
instantiatedClocks(freqMHz)._1
|
||||
}
|
||||
for ((name, (freq, clock)) <- clockMap) {
|
||||
val freqMHz = (freq / (1000 * 1000)).toInt
|
||||
clock := findOrInstantiate(freqMHz, name)
|
||||
}
|
||||
|
||||
_buildTopClockRecord.get
|
||||
}
|
||||
// The undivided reference clock as calculated by pllConfig must be instantiated
|
||||
findOrInstantiate(pllConfig.referenceFreqMHz.toInt, "reference")
|
||||
|
||||
/**
|
||||
* Connect all clocks requested to ClockBridge
|
||||
*/
|
||||
def instantiateFireSimClockBridge: Unit = {
|
||||
require(_buildTopClockParams.isDefined, "Must have rational clocks to assign to")
|
||||
val BuildTopClockParameters(allClocks, refRatClockName, refRatClockFreq) = _buildTopClockParams.get
|
||||
require(_buildTopClockMap.exists(_._1 == refRatClockName),
|
||||
s"Provided base-clock name for rational clocks, ${refRatClockName}, doesn't match a name within specified rational clocks." +
|
||||
"Available clocks:\n " + _buildTopClockMap.map(_._1).mkString("\n "))
|
||||
|
||||
// Simplify the RationalClocks ratio's
|
||||
val refRatClock = _buildTopClockMap.find(_._1 == refRatClockName).get._2._1
|
||||
val simpleRatClocks = _buildTopClockMap.map { t =>
|
||||
val ratClock = t._2._1
|
||||
ratClock.copy(
|
||||
multiplier = ratClock.multiplier * refRatClock.divisor,
|
||||
divisor = ratClock.divisor * refRatClock.multiplier).simplify
|
||||
}
|
||||
|
||||
// Determine all the clock dividers (harness + rational clocks)
|
||||
// Note: Requires that the BuildTop reference frequency is requested with proper freq.
|
||||
val refRatSinkParams = ClockSinkParameters(take=Some(ClockParameters(freqMHz=refRatClockFreq / (1000 * 1000))),name=Some(refRatClockName))
|
||||
val harSinkParams = _harnessClockMap.map { case (name, (freq, bundle)) =>
|
||||
ClockSinkParameters(take=Some(ClockParameters(freqMHz=freq / (1000 * 1000))),name=Some(name))
|
||||
val ratClocks = instantiatedClocks.map { case (freqMHz, (clock, names)) =>
|
||||
(RationalClock(names.mkString(","), 1, pllConfig.referenceFreqMHz.toInt / freqMHz), clock)
|
||||
}.toSeq
|
||||
val allSinkParams = harSinkParams :+ refRatSinkParams
|
||||
|
||||
// Use PLL config to determine overall div's
|
||||
val pllConfig = new SimplePllConfiguration("firesimOverallClockBridge", allSinkParams)
|
||||
pllConfig.emitSummaries
|
||||
|
||||
// Adjust all BuildTop RationalClocks with the div determined by the PLL
|
||||
val refRatDiv = pllConfig.sinkDividerMap(refRatSinkParams)
|
||||
val adjRefRatClocks = simpleRatClocks.map { clock =>
|
||||
clock.copy(divisor = clock.divisor * refRatDiv).simplify
|
||||
}
|
||||
|
||||
// Convert harness clocks to RationalClocks
|
||||
val harRatClocks = harSinkParams.map { case ClockSinkParameters(_, _, _, _, clkParamsOpt, nameOpt) =>
|
||||
RationalClock(nameOpt.get, 1, pllConfig.referenceFreqMHz.toInt / clkParamsOpt.get.freqMHz.toInt)
|
||||
}
|
||||
|
||||
val allAdjRatClks = adjRefRatClocks ++ harRatClocks
|
||||
|
||||
// Removes clocks that have the same frequency before instantiating the
|
||||
// clock bridge to avoid unnecessary BUFGCE use.
|
||||
val allDistinctRatClocks = allAdjRatClks.foldLeft(Seq(RationalClock(pllConfig.referenceSinkParams.name.get, 1, 1))) {
|
||||
case (list, candidate) => if (list.exists { clock => clock.equalFrequency(candidate) }) list else list :+ candidate
|
||||
}
|
||||
|
||||
val clockBridge = Module(new RationalClockBridge(allDistinctRatClocks))
|
||||
val cbVecTuples = allDistinctRatClocks.zip(clockBridge.io.clocks)
|
||||
|
||||
// Connect all clocks (harness + BuildTop clocks)
|
||||
for (clock <- allAdjRatClks) {
|
||||
val (_, cbClockField) = cbVecTuples.find(_._1.equalFrequency(clock)).get
|
||||
_buildTopClockMap.get(clock.name).map { case (_, clk) => clk := cbClockField }
|
||||
_harnessClockMap.get(clock.name).map { case (_, clk) => clk := cbClockField }
|
||||
val clockBridge = Module(new RationalClockBridge(ratClocks.map(_._1)))
|
||||
(clockBridge.io.clocks zip ratClocks).foreach { case (clk, rat) =>
|
||||
rat._2 := clk
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case object ClockBridgeInstantiatorKey extends Field[ClockBridgeInstantiator](new ClockBridgeInstantiator)
|
||||
case object FireSimBaseClockNameKey extends Field[String]("implicit_clock")
|
||||
|
||||
class ClocksWithSinkParams(val params: Seq[ClockSinkParameters]) extends Bundle {
|
||||
val clocks = Vec(params.size, Clock())
|
||||
}
|
||||
|
||||
class WithFireSimSimpleClocks extends OverrideLazyIOBinder({
|
||||
(system: HasChipyardPRCI) => {
|
||||
implicit val p = GetSystemParameters(system)
|
||||
// Figure out what provides this in the chipyard scheme
|
||||
implicit val valName = ValName("FireSimClocking")
|
||||
|
||||
val implicitClockSinkNode = ClockSinkNode(Seq(ClockSinkParameters(name = Some("implicit_clock"))))
|
||||
system.connectImplicitClockSinkNode(implicitClockSinkNode)
|
||||
InModuleBody {
|
||||
val implicit_clock = implicitClockSinkNode.in.head._1.clock
|
||||
val implicit_reset = implicitClockSinkNode.in.head._1.reset
|
||||
system.asInstanceOf[BaseSubsystem].module match { case l: LazyModuleImp => {
|
||||
l.clock := implicit_clock
|
||||
l.reset := implicit_reset
|
||||
}}
|
||||
}
|
||||
|
||||
val inputClockSource = ClockGroupSourceNode(Seq(ClockGroupSourceParameters()))
|
||||
system.allClockGroupsNode := inputClockSource
|
||||
|
||||
InModuleBody {
|
||||
val (clockGroupBundle, clockGroupEdge) = inputClockSource.out.head
|
||||
val reset_io = IO(Input(AsyncReset())).suggestName("async_reset")
|
||||
|
||||
val input_clocks = IO(Input(new ClocksWithSinkParams(clockGroupEdge.sink.members)))
|
||||
.suggestName("clocks")
|
||||
|
||||
(clockGroupBundle.member.data zip input_clocks.clocks).foreach { case (clockBundle, inputClock) =>
|
||||
clockBundle.clock := inputClock
|
||||
clockBundle.reset := reset_io
|
||||
}
|
||||
|
||||
(Seq(reset_io, input_clocks), Nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
class WithFireSimHarnessClockBinder extends OverrideHarnessBinder({
|
||||
(system: HasChipyardPRCI, th: FireSim, ports: Seq[Data]) => {
|
||||
implicit val p = th.p
|
||||
ports.map ({
|
||||
case c: ClocksWithSinkParams => {
|
||||
val pllConfig = new SimplePllConfiguration("firesimBuildTopClockGenerator", c.params)
|
||||
pllConfig.emitSummaries
|
||||
th.setRefClockFreq(pllConfig.referenceFreqMHz)
|
||||
val rationalClockSpecs = for ((sinkP, division) <- pllConfig.sinkDividerMap) yield {
|
||||
RationalClock(sinkP.name.get, 1, division)
|
||||
}
|
||||
val input_clocks: RecordMap[Clock] = p(ClockBridgeInstantiatorKey).requestClockRecordMap(
|
||||
BuildTopClockParameters(
|
||||
rationalClockSpecs.toSeq,
|
||||
p(FireSimBaseClockNameKey),
|
||||
pllConfig.referenceFreqMHz * (1000 * 1000)))
|
||||
(c.clocks zip c.params) map ({ case (clock, param) =>
|
||||
clock := input_clocks(param.name.get).get
|
||||
})
|
||||
}
|
||||
case r: Reset => r := th.buildtopReset.asAsyncReset
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessSignalReferences {
|
||||
class FireSim(implicit val p: Parameters) extends RawModule with HasHarnessInstantiators {
|
||||
require(harnessClockInstantiator.isInstanceOf[FireSimClockBridgeInstantiator])
|
||||
freechips.rocketchip.util.property.cover.setPropLib(new midas.passes.FireSimPropertyLibrary())
|
||||
|
||||
val buildtopClock = Wire(Clock())
|
||||
val buildtopReset = WireInit(false.B)
|
||||
// The peek-poke bridge must still be instantiated even though it's
|
||||
// functionally unused. This will be removed in a future PR.
|
||||
val dummy = WireInit(false.B)
|
||||
val peekPokeBridge = PeekPokeBridge(buildtopClock, dummy)
|
||||
val peekPokeBridge = PeekPokeBridge(harnessBinderClock, dummy)
|
||||
|
||||
val resetBridge = Module(new ResetPulseBridge(ResetPulseBridgeParameters()))
|
||||
// In effect, the bridge counts the length of the reset in terms of this clock.
|
||||
resetBridge.io.clock := buildtopClock
|
||||
buildtopReset := resetBridge.io.reset
|
||||
// Ensures FireSim-synthesized assertions and instrumentation is disabled
|
||||
// while buildtopReset is asserted. This ensures assertions do not fire at
|
||||
// time zero in the event their local reset is delayed (typically because it
|
||||
// has been pipelined)
|
||||
midas.targetutils.GlobalResetCondition(buildtopReset)
|
||||
resetBridge.io.clock := harnessBinderClock
|
||||
|
||||
def dutReset = { require(false, "dutReset should not be used in Firesim"); false.B }
|
||||
def referenceClockFreqMHz = 0.0
|
||||
def referenceClock = false.B.asClock // unused
|
||||
def referenceReset = resetBridge.io.reset
|
||||
def success = { require(false, "success should not be used in Firesim"); false.B }
|
||||
|
||||
// Instantiate multiple instances of the DUT to implement supernode
|
||||
for (i <- 0 until p(NumNodes)) {
|
||||
// It's not a RC bump without some hacks...
|
||||
// Copy the AsyncClockGroupsKey to generate a fresh node on each
|
||||
// instantiation of the dut, otherwise the initial instance will be
|
||||
// reused across each node
|
||||
import freechips.rocketchip.subsystem.AsyncClockGroupsKey
|
||||
val lazyModule = LazyModule(p(BuildTop)(p))
|
||||
val module = Module(lazyModule.module)
|
||||
override val supportsMultiChip = true
|
||||
|
||||
lazyModule match { case d: HasIOBinders =>
|
||||
ApplyHarnessBinders(this, d.lazySystem, d.portMap)
|
||||
}
|
||||
NodeIdx.increment()
|
||||
}
|
||||
instantiateChipTops()
|
||||
|
||||
buildtopClock := p(ClockBridgeInstantiatorKey).requestClock("buildtop_reference_clock", getRefClockFreq * (1000 * 1000))
|
||||
|
||||
p(ClockBridgeInstantiatorKey).instantiateFireSimClockBridge
|
||||
// Ensures FireSim-synthesized assertions and instrumentation is disabled
|
||||
// while resetBridge.io.reset is asserted. This ensures assertions do not fire at
|
||||
// time zero in the event their local reset is delayed (typically because it
|
||||
// has been pipelined)
|
||||
midas.targetutils.GlobalResetCondition(resetBridge.io.reset)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import sifive.blocks.devices.uart.{PeripheryUARTKey, UARTParams}
|
||||
import scala.math.{min, max}
|
||||
|
||||
import chipyard.clocking.{ChipyardPRCIControlKey}
|
||||
import chipyard.harness.{HarnessClockInstantiatorKey}
|
||||
import icenet._
|
||||
|
||||
import firesim.bridges._
|
||||
@@ -43,6 +44,11 @@ class WithoutClockGating extends Config((site, here, up) => {
|
||||
case ChipyardPRCIControlKey => up(ChipyardPRCIControlKey, site).copy(enableTileClockGating = false)
|
||||
})
|
||||
|
||||
// Use the firesim clock bridge instantiator. this is required
|
||||
class WithFireSimHarnessClockBridgeInstantiator extends Config((site, here, up) => {
|
||||
case HarnessClockInstantiatorKey => () => new FireSimClockBridgeInstantiator
|
||||
})
|
||||
|
||||
// Testing configurations
|
||||
// This enables printfs used in testing
|
||||
class WithScalaTestFeatures extends Config((site, here, up) => {
|
||||
@@ -63,9 +69,11 @@ class WithNVDLASmall extends nvidia.blocks.dla.WithNVDLA("small")
|
||||
|
||||
// Minimal set of FireSim-related design tweaks - notably discludes FASED, TraceIO, and the BlockDevice
|
||||
class WithMinimalFireSimDesignTweaks extends Config(
|
||||
// Required*: Uses FireSim ClockBridge and PeekPokeBridge to drive the system with a single clock/reset
|
||||
new WithFireSimHarnessClockBinder ++
|
||||
new WithFireSimSimpleClocks ++
|
||||
// Required*: Punch all clocks to FireSim's harness clock instantiator
|
||||
new WithFireSimHarnessClockBridgeInstantiator ++
|
||||
new chipyard.harness.WithHarnessBinderClockFreqMHz(1000.0) ++
|
||||
new chipyard.harness.WithClockAndResetFromHarness ++
|
||||
new chipyard.clocking.WithPassthroughClockGenerator ++
|
||||
// Required*: When using FireSim-as-top to provide a correct path to the target bootrom source
|
||||
new WithBootROM ++
|
||||
// Required: Existing FAME-1 transform cannot handle black-box clock gates
|
||||
@@ -84,7 +92,7 @@ class WithFireSimDesignTweaks extends Config(
|
||||
// Optional: reduce the width of the Serial TL interface
|
||||
new testchipip.WithSerialTLWidth(4) ++
|
||||
// Required*: Scale default baud rate with periphery bus frequency
|
||||
new chipyard.config.WithUART(BigInt(3686400L)) ++
|
||||
new chipyard.config.WithUARTInitBaudRate(BigInt(3686400L)) ++
|
||||
// Optional: Adds IO to attach tracerV bridges
|
||||
new chipyard.config.WithTraceIO ++
|
||||
// Optional: Request 16 GiB of target-DRAM by default (can safely request up to 32 GiB on F1)
|
||||
@@ -95,11 +103,15 @@ class WithFireSimDesignTweaks extends Config(
|
||||
|
||||
// Tweaks to modify target clock frequencies / crossings to legacy firesim defaults
|
||||
class WithFireSimHighPerfClocking extends Config(
|
||||
// Create clock group for uncore that does not include mbus
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName(("uncore", Seq("sbus", "pbus", "fbus", "cbus", "implicit"), Nil)) ++
|
||||
// Optional: This sets the default frequency for all buses in the system to 3.2 GHz
|
||||
// (since unspecified bus frequencies will use the pbus frequency)
|
||||
// This frequency selection matches FireSim's legacy selection and is required
|
||||
// to support 200Gb NIC performance. You may select a smaller value.
|
||||
new chipyard.config.WithPeripheryBusFrequency(3200.0) ++
|
||||
new chipyard.config.WithSystemBusFrequency(3200.0) ++
|
||||
new chipyard.config.WithFrontBusFrequency(3200.0) ++
|
||||
// Optional: These three configs put the DRAM memory system in it's own clock domain.
|
||||
// Removing the first config will result in the FASED timing model running
|
||||
// at the pbus freq (above, 3.2 GHz), which is outside the range of valid DDR3 speedgrades.
|
||||
@@ -116,25 +128,17 @@ class WithFireSimConfigTweaks extends Config(
|
||||
// Using some other frequency will require runnings the FASED runtime configuration generator
|
||||
// to generate faithful DDR3 timing values.
|
||||
new chipyard.config.WithSystemBusFrequency(1000.0) ++
|
||||
new chipyard.config.WithSystemBusFrequencyAsDefault ++ // All unspecified clock frequencies, notably the implicit clock, will use the sbus freq (1000 MHz)
|
||||
// Explicitly set PBUS + MBUS to 1000 MHz, since they will be driven to 100 MHz by default because of assignments in the Chisel
|
||||
new chipyard.config.WithPeripheryBusFrequency(1000.0) ++
|
||||
new chipyard.config.WithMemoryBusFrequency(1000.0) ++
|
||||
new WithFireSimDesignTweaks
|
||||
)
|
||||
|
||||
// Tweak more representative of testchip configs
|
||||
class WithFireSimTestChipConfigTweaks extends Config(
|
||||
new chipyard.config.WithTestChipBusFreqs ++
|
||||
new WithFireSimDesignTweaks
|
||||
)
|
||||
|
||||
// Tweaks to use minimal design tweaks
|
||||
// Need to use initramfs to use linux (no block device)
|
||||
class WithMinimalFireSimHighPerfConfigTweaks extends Config(
|
||||
new WithFireSimHighPerfClocking ++
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++
|
||||
new testchipip.WithBackingScratchpad ++
|
||||
new testchipip.WithMbusScratchpad ++
|
||||
new WithMinimalFireSimDesignTweaks
|
||||
)
|
||||
|
||||
@@ -144,7 +148,7 @@ class WithMinimalFireSimHighPerfConfigTweaks extends Config(
|
||||
class WithMinimalAndBlockDeviceFireSimHighPerfConfigTweaks extends Config(
|
||||
new WithFireSimHighPerfClocking ++
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++ // removes mem port for FASEDBridge to match against
|
||||
new testchipip.WithBackingScratchpad ++ // adds backing scratchpad for memory to replace FASED model
|
||||
new testchipip.WithMbusScratchpad ++ // adds backing scratchpad for memory to replace FASED model
|
||||
new testchipip.WithBlockDevice(true) ++ // add in block device
|
||||
new WithMinimalFireSimDesignTweaks
|
||||
)
|
||||
@@ -164,6 +168,23 @@ class WithFireSimHighPerfConfigTweaks extends Config(
|
||||
new WithFireSimDesignTweaks
|
||||
)
|
||||
|
||||
// Tweak more representative of testchip configs
|
||||
class WithFireSimTestChipConfigTweaks extends Config(
|
||||
// Frequency specifications
|
||||
new chipyard.config.WithTileFrequency(1000.0) ++ // Realistic tile frequency for a test chip
|
||||
new chipyard.config.WithSystemBusFrequency(500.0) ++ // Realistic system bus frequency
|
||||
new chipyard.config.WithMemoryBusFrequency(1000.0) ++ // Needs to be 1000 MHz to model DDR performance accurately
|
||||
new chipyard.config.WithPeripheryBusFrequency(500.0) ++ // Match the sbus and pbus frequency
|
||||
new chipyard.clocking.WithClockGroupsCombinedByName(("uncore", Seq("sbus", "pbus", "fbus", "cbus", "implicit"), Seq("tile"))) ++
|
||||
// Crossing specifications
|
||||
new chipyard.config.WithCbusToPbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossing between PBUS and CBUS
|
||||
new chipyard.config.WithSbusToMbusCrossingType(AsynchronousCrossing()) ++ // Add Async crossings between backside of L2 and MBUS
|
||||
new freechips.rocketchip.subsystem.WithRationalRocketTiles ++ // Add rational crossings between RocketTile and uncore
|
||||
new boom.common.WithRationalBoomTiles ++ // Add rational crossings between BoomTile and uncore
|
||||
new testchipip.WithAsynchronousSerialSlaveCrossing ++ // Add Async crossing between serial and MBUS. Its master-side is tied to the FBUS
|
||||
new WithFireSimDesignTweaks
|
||||
)
|
||||
|
||||
/*******************************************************************************
|
||||
* Full TARGET_CONFIG configurations. These set parameters of the target being
|
||||
* simulated.
|
||||
@@ -186,6 +207,22 @@ class FireSimRocketConfig extends Config(
|
||||
new chipyard.RocketConfig)
|
||||
// DOC include end: firesimconfig
|
||||
|
||||
class FireSimRocket1GiBDRAMConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 1L) ++
|
||||
new FireSimRocketConfig)
|
||||
|
||||
class FireSimRocketMMIOOnly1GiBDRAMConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 1L) ++
|
||||
new FireSimRocketMMIOOnlyConfig)
|
||||
|
||||
class FireSimRocket4GiBDRAMConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 4L) ++
|
||||
new FireSimRocketConfig)
|
||||
|
||||
class FireSimRocketMMIOOnly4GiBDRAMConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 4L) ++
|
||||
new FireSimRocketMMIOOnlyConfig)
|
||||
|
||||
class FireSimQuadRocketConfig extends Config(
|
||||
new WithDefaultFireSimBridges ++
|
||||
new WithDefaultMemModel ++
|
||||
@@ -204,7 +241,7 @@ class FireSimSmallSystemConfig extends Config(
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize(1 << 28) ++
|
||||
new testchipip.WithDefaultSerialTL ++
|
||||
new testchipip.WithBlockDevice ++
|
||||
new chipyard.config.WithUART ++
|
||||
new chipyard.config.WithUARTInitBaudRate(BigInt(3686400L)) ++
|
||||
new freechips.rocketchip.subsystem.WithInclusiveCache(nWays = 2, capacityKB = 64) ++
|
||||
new chipyard.RocketConfig)
|
||||
|
||||
@@ -251,9 +288,10 @@ class FireSimLeanGemminiPrintfRocketConfig extends Config(
|
||||
// Supernode Configurations, base off chipyard's RocketConfig
|
||||
//**********************************************************************************
|
||||
class SupernodeFireSimRocketConfig extends Config(
|
||||
new WithNumNodes(4) ++
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 8L) ++ // 8 GB
|
||||
new FireSimRocketConfig)
|
||||
new WithFireSimHarnessClockBridgeInstantiator ++
|
||||
new chipyard.harness.WithHomogeneousMultiChip(n=4, new Config(
|
||||
new freechips.rocketchip.subsystem.WithExtMemSize((1 << 30) * 8L) ++ // 8GB DRAM per node
|
||||
new FireSimRocketConfig)))
|
||||
|
||||
//**********************************************************************************
|
||||
//* CVA6 Configurations
|
||||
@@ -291,7 +329,7 @@ class FireSim16LargeBoomConfig extends Config(
|
||||
class FireSimNoMemPortConfig extends Config(
|
||||
new WithDefaultFireSimBridges ++
|
||||
new freechips.rocketchip.subsystem.WithNoMemPort ++
|
||||
new testchipip.WithBackingScratchpad ++
|
||||
new testchipip.WithMbusScratchpad ++
|
||||
new WithFireSimConfigTweaks ++
|
||||
new chipyard.RocketConfig)
|
||||
|
||||
|
||||
Submodule generators/gemmini updated: 965ea0b3c5...f13847e839
Submodule generators/ibex updated: 916fb7a6ff...66ec6e56ed
Submodule generators/icenet updated: ce1ec55c1f...68b4c7f30f
Submodule generators/mempress updated: 295ae0854a...c3d4901ab0
Submodule generators/nvdla updated: 7130a5c0f7...730fad4360
Submodule generators/riscv-sodor updated: c051956d3b...c1c809ebd5
Submodule generators/rocket-chip updated: ec0e8dcaf8...2e37b25529
Submodule generators/sha3 updated: 1fa5ef8ae5...eb3822a2bc
1
generators/shuttle
Submodule
1
generators/shuttle
Submodule
Submodule generators/shuttle added at 3c15591a9e
Submodule generators/sifive-blocks updated: 534d3b74a0...abf129a33b
Submodule generators/testchipip updated: efdef96772...1952231569
Reference in New Issue
Block a user