Merge remote-tracking branch 'origin/main' into rcbump

This commit is contained in:
Jerry Zhao
2023-07-25 09:30:19 -07:00
33 changed files with 1788 additions and 1027 deletions

View File

@@ -35,6 +35,11 @@ extern std::map<long long int, backing_data_t> backing_mem_data;
#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;
int pmpregions;
@@ -42,19 +47,21 @@ 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(size), name(n) { };
bool load(reg_t addr, size_t len, uint8_t* bytes) {
if (addr + len < addr || addr + len > size) return false;
printf("Read from device %s at %lx\n", name.c_str(), addr);
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;
}
bool store(reg_t addr, size_t len, const uint8_t* bytes) {
return (addr + len >= addr && addr + len <= size);
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:
@@ -70,7 +77,7 @@ reg_t fromhost_addr = 0;
reg_t cospike_timeout = 0;
std::set<reg_t> magic_addrs;
cfg_t* cfg;
std::vector<read_override_device_t*> read_override_devices;
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)
{
@@ -82,7 +89,7 @@ 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
@@ -91,6 +98,7 @@ extern "C" void cospike_set_sysinfo(char* isa, int pmpregions,
info = new system_info_t;
// 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;
@@ -118,7 +126,7 @@ extern "C" void cospike_cosim(long long int cycle,
assert(info);
if (unlikely(!sim)) {
printf("Configuring spike cosim\n");
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));
@@ -128,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,
@@ -146,26 +154,26 @@ extern "C" void cospike_cosim(long long int cycle,
assert(info->bootrom.size() < default_boot_rom_size);
info->bootrom.resize(default_boot_rom_size);
rom_device_t *boot_rom = new rom_device_t(info->bootrom);
mem_t *boot_addr_reg = new mem_t(0x1000);
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));
read_override_device_t* clint = new read_override_device_t("clint", CLINT_SIZE);
read_override_device_t* uart = new read_override_device_t("uart", UART_SIZE);
read_override_device_t* plic = new read_override_device_t("plic", PLIC_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);
read_override_devices.push_back(clint);
read_override_devices.push_back(uart);
read_override_devices.push_back(plic);
std::vector<std::pair<reg_t, abstract_device_t*>> plugin_devices;
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(default_boot_rom_addr, boot_rom));
plugin_devices.push_back(std::pair(CLINT_BASE, clint));
plugin_devices.push_back(std::pair(UART_BASE, uart));
plugin_devices.push_back(std::pair(PLIC_BASE, plic));
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))
@@ -200,16 +208,17 @@ extern "C" void cospike_cosim(long long int cycle,
.support_impebreak = true
};
printf("isa string: %s\n", info->isa.c_str());
printf("htif args: ");
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", htif_args[i].c_str());
COSPIKE_PRINTF("%s", htif_args[i].c_str());
}
printf("\n");
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,
@@ -218,6 +227,8 @@ 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
@@ -227,9 +238,9 @@ extern "C" void cospike_cosim(long long int cycle,
for (auto& pair : backing_mem_data) {
size_t base = pair.first;
size_t size = pair.second.size;
printf("Matching spike memory initial state for region %lx-%lx\n", base, base + 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)) {
printf("Error, unable to match memory at address %lx\n", base);
COSPIKE_PRINTF("Error, unable to match memory at address %lx\n", base);
abort();
}
}
@@ -249,17 +260,17 @@ extern "C" void cospike_cosim(long long int cycle,
sim->set_debug(cospike_debug);
sim->set_histogram(true);
sim->set_procs_debug(cospike_debug);
printf("Setting up htif for spike cosim\n");
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);
printf("BootROM base : %lx\n", default_boot_rom_addr);
printf("BootROM size : %lx\n", boot_rom->contents().size());
printf("Memory base : %lx\n", info->mem0_base);
printf("Memory size : %lx\n", info->mem0_size);
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
@@ -268,7 +279,7 @@ extern "C" void cospike_cosim(long long int cycle,
if (cospike_timeout && cycle > cospike_timeout) {
if (sim) {
printf("Cospike reached timeout cycles = %ld, terminating\n", cospike_timeout);
COSPIKE_PRINTF("Cospike reached timeout cycles = %ld, terminating\n", cospike_timeout);
delete sim;
}
exit(0);
@@ -279,7 +290,7 @@ extern "C" void cospike_cosim(long long int cycle,
state_t* s = p->get_state();
#ifdef COSPIKE_DTM
if (dtm && dtm->loadarch_done && !spike_loadarch_done) {
printf("Restoring spike state from testchip_dtm loadarch\n");
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;
@@ -311,11 +322,11 @@ extern "C" void cospike_cosim(long long int cycle,
RESTORE(CSR_MCYCLE , mcycle);
RESTORE(CSR_MINSTRET , minstret);
if (ls.VLEN != p->VU.VLEN) {
printf("VLEN mismatch loadarch: $d != spike: $d\n", 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) {
printf("ELEN mismatch loadarch: $d != spike: $d\n", 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++) {
@@ -335,7 +346,7 @@ extern "C" void cospike_cosim(long long int cycle,
bool mtip_interrupt = interrupt_cause == 0x7;
bool debug_interrupt = interrupt_cause == 0xe;
if (raise_interrupt) {
printf("%d interrupt %lx\n", cycle, cause);
COSPIKE_PRINTF("%d interrupt %lx\n", cycle, cause);
if (ssip_interrupt || stip_interrupt) {
// do nothing
@@ -346,41 +357,41 @@ extern "C" void cospike_cosim(long long int cycle,
} 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) {
p->clear_waiting_for_interrupt();
printf("%d Cosim: %lx", cycle, iaddr);
COSPIKE_PRINTF("%d Cosim: %lx", cycle, iaddr);
// if (has_wdata) {
// printf(" s: %lx", wdata);
// COSPIKE_PRINTF(" s: %lx", wdata);
// }
printf("\n");
COSPIKE_PRINTF("\n");
}
if (valid || raise_interrupt || raise_exception) {
p->clear_waiting_for_interrupt();
for (auto& e : read_override_devices) e->was_read_from = false;
for (auto& e : read_override_devices) e.get()->was_read_from = false;
p->step(1);
if (unlikely(cospike_debug)) {
printf("spike pc is %lx\n", s->pc);
printf("spike mstatus is %lx\n", s->mstatus->read());
printf("spike mip is %lx\n", s->mip->read());
printf("spike mie is %lx\n", s->mie->read());
printf("spike wfi state is %d\n", p->is_waiting_for_interrupt());
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 && !raise_exception) {
if (s_pc != iaddr) {
printf("%d PC mismatch spike %llx != DUT %llx\n", cycle, s_pc, iaddr);
COSPIKE_PRINTF("%d PC mismatch spike %llx != DUT %llx\n", cycle, s_pc, iaddr);
if (unlikely(cospike_debug)) {
printf("spike mstatus is %lx\n", s->mstatus->read());
printf("spike mcause is %lx\n", s->mcause->read());
printf("spike mtval is %lx\n" , s->mtval->read());
printf("spike mtinst is %lx\n", s->mtinst->read());
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);
}
@@ -401,7 +412,7 @@ extern "C" void cospike_cosim(long long int cycle,
}
// 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)) {
printf("Probable magic mem %lx\n", w_data);
COSPIKE_PRINTF("Probable magic mem %lx\n", w_data);
magic_addrs.insert(w_data);
}
}
@@ -424,7 +435,7 @@ extern "C" void cospike_cosim(long long int cycle,
// 3 => vec hint
// 4 => csr
bool device_read = false;
for (auto& e : read_override_devices) if (e->was_read_from) device_read = true;
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);
@@ -449,7 +460,7 @@ extern "C" void cospike_cosim(long long int cycle,
uint64_t csr_addr = (insn >> 20) & 0xfff;
bool csr_read = (insn & 0x7f) == 0x73;
if (csr_read)
printf("CSR read %lx\n", csr_addr);
COSPIKE_PRINTF("CSR read %lx\n", csr_addr);
if (csr_read && ((csr_addr == 0x301) || // misa
(csr_addr == 0x306) || // mcounteren
(csr_addr == 0xf13) || // mimpid
@@ -463,17 +474,17 @@ extern "C" void cospike_cosim(long long int cycle,
(csr_addr >= 0x7a0 && csr_addr <= 0x7aa) || // debug trigger registers
(csr_addr >= 0x3b0 && csr_addr <= 0x3ef) // pmpaddr
)) {
printf("CSR override\n");
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
printf("Read override %lx = %lx\n", mem_read_addr, wdata);
COSPIKE_PRINTF("Read override %lx = %lx\n", mem_read_addr, wdata);
s->XPR.write(rd, wdata);
} else if (wdata != regwrite.second.v[0]) {
printf("%d wdata mismatch reg %d %lx != %lx\n", cycle, rd,
COSPIKE_PRINTF("%d wdata mismatch reg %d %lx != %lx\n", cycle, rd,
regwrite.second.v[0], wdata);
exit(1);
}
@@ -481,7 +492,7 @@ extern "C" void cospike_cosim(long long int cycle,
// TODO FIX: Rocketchip TracedInstruction.wdata should be Valid(UInt)
// if (scalar_wb ^ has_wdata) {
// printf("Scalar wdata behavior divergence between spike and DUT\n");
// COSPIKE_PRINTF("Scalar wdata behavior divergence between spike and DUT\n");
// exit(-1);
// }
}

View File

@@ -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,
@@ -23,6 +24,7 @@ import "DPI-C" function void cospike_cosim(input longint cycle,
module SpikeCosim #(
parameter ISA,
parameter PRIV,
parameter PMPREGIONS,
parameter MEM0_BASE,
parameter MEM0_SIZE,
@@ -57,7 +59,7 @@ module SpikeCosim #(
);
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

View File

@@ -14,6 +14,7 @@ import testchipip.TileTraceIO
case class SpikeCosimConfig(
isa: String,
priv: String,
pmpregions: Int,
mem0_base: BigInt,
mem0_size: BigInt,
@@ -24,6 +25,7 @@ case class SpikeCosimConfig(
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),

View File

@@ -6,7 +6,8 @@ import chisel3.util.{log2Up}
import org.chipsalliance.cde.config.{Config}
import freechips.rocketchip.devices.tilelink.{BootROMLocated, PLICKey, CLINTKey}
import freechips.rocketchip.devices.debug.{Debug, ExportDebug, DebugModuleKey, DMI}
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,49 +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) => {
class WithUARTInitBaudRate(baudrate: BigInt = 115200) extends Config ((site, here, up) => {
case PeripheryUARTKey => up(PeripheryUARTKey).map(_.copy(initBaudRate=baudrate))
})
/**
* 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 => Seq(
SPIFlashParams(rAddress = 0x10040000, fAddress = 0x20000000, fSize = size))
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 WithDMIDTM extends Config((site, here, up) => {
case ExportDebug => up(ExportDebug, site).copy(protocols = Set(DMI))
})
class WithNoDebug extends Config((site, here, up) => {
case DebugModuleKey => None
/**
* 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) => {

View File

@@ -365,6 +365,7 @@ class WithCospike extends ComposeHarnessBinder({
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),

View File

@@ -34,5 +34,5 @@ class TestHarness(implicit val p: Parameters) extends Module with HasHarnessInst
def referenceClock = clock
def referenceReset = reset
instantiateChipTops()
val lazyDuts = instantiateChipTops()
}

View 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)

View 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
}

View 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
}
}

View 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)

View File

@@ -92,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)
@@ -241,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)