Compare commits
58 Commits
8c6e745653
...
6df42fc360
Author | SHA1 | Date | |
---|---|---|---|
6df42fc360 | |||
4ba8acb4aa | |||
12583af4a8 | |||
4cfae27efd | |||
7f96da2288 | |||
7593baf2aa | |||
f48c2767d7 | |||
894960678c | |||
70895b6ffa | |||
12997a644d | |||
3cb9e57b5e | |||
2489a08328 | |||
9a56e44e32 | |||
d6bc9c53f0 | |||
78dad3e89b | |||
4e11491531 | |||
1c1b6e8ffe | |||
d00a0bba32 | |||
59d5e61366 | |||
ea89259dd4 | |||
15e058e3da | |||
d889a0ca16 | |||
e0c3c63826 | |||
0fcacd37df | |||
7458378a4a | |||
3063fd1b46 | |||
2f239f2a9a | |||
e07b37c7ad | |||
d3c16258fd | |||
0e0963d360 | |||
99862942fe | |||
1b93b27da4 | |||
933f2ce958 | |||
d6e2c1a73f | |||
32592377c6 | |||
8bb397a1b9 | |||
7d146f3401 | |||
ef7a6115b7 | |||
15dc7f6760 | |||
42e614550c | |||
64b707cbb6 | |||
d0b46c5b8f | |||
f1bd9c99aa | |||
f00e9576e3 | |||
b669fb3d6a | |||
2a0e67ab15 | |||
a3d99e5ba2 | |||
a20998e215 | |||
8856953905 | |||
4256d99a9b | |||
41d1a62713 | |||
bd3a72e585 | |||
e3be5db3e6 | |||
878a357a0d | |||
5eae81038d | |||
644ba6dafa | |||
904f0f3d93 | |||
42e5e92d43 |
@ -15,7 +15,7 @@ lazy val commonSettings = Seq(
|
||||
traceLevel := 15,
|
||||
scalacOptions ++= Seq("-deprecation","-unchecked"),
|
||||
libraryDependencies ++= Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value),
|
||||
libraryDependencies ++= Seq("org.json4s" %% "json4s-jackson" % "3.5.0"),
|
||||
libraryDependencies ++= Seq("org.json4s" %% "json4s-jackson" % "3.5.3"),
|
||||
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
|
||||
)
|
||||
|
||||
|
2
chisel3
2
chisel3
Submodule chisel3 updated: 97871178cb...531dd6cb7a
@ -3,6 +3,7 @@
|
||||
|
||||
#include "verilated.h"
|
||||
#if VM_TRACE
|
||||
#include <memory>
|
||||
#include "verilated_vcd_c.h"
|
||||
#endif
|
||||
#include <fesvr/dtm.h>
|
||||
|
430
csrc/jtag_vpi.c
430
csrc/jtag_vpi.c
@ -1,430 +0,0 @@
|
||||
/*
|
||||
* TCP/IP controlled VPI JTAG Interface.
|
||||
* Based on Julius Baxter's work on jp_vpi.c
|
||||
*
|
||||
* Copyright (C) 2012 Franck Jullien, <franck.jullien@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of any
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <vpi_user.h>
|
||||
|
||||
// Calling bind with port 0 will return a socket bound to an unused port.
|
||||
#define RSP_SERVER_PORT 0
|
||||
#define XFERT_MAX_SIZE 512
|
||||
|
||||
const char * cmd_to_string[] = {"CMD_RESET",
|
||||
"CMD_TMS_SEQ",
|
||||
"CMD_SCAN_CHAIN"};
|
||||
|
||||
struct vpi_cmd {
|
||||
int cmd;
|
||||
unsigned char buffer_out[XFERT_MAX_SIZE];
|
||||
unsigned char buffer_in[XFERT_MAX_SIZE];
|
||||
int length;
|
||||
int nb_bits;
|
||||
};
|
||||
|
||||
int listenfd = 0;
|
||||
int connfd = 0;
|
||||
|
||||
int init_jtag_server(int port)
|
||||
{
|
||||
struct sockaddr_in serv_addr;
|
||||
int flags;
|
||||
|
||||
listenfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
memset(&serv_addr, '0', sizeof(serv_addr));
|
||||
|
||||
serv_addr.sin_family = AF_INET;
|
||||
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
serv_addr.sin_port = htons(port);
|
||||
|
||||
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
|
||||
|
||||
listen(listenfd, 10);
|
||||
|
||||
socklen_t socklen = sizeof(serv_addr);
|
||||
if (getsockname(listenfd, (struct sockaddr *)&serv_addr, &socklen) == -1) {
|
||||
perror("init_jtag_server");
|
||||
fflush(stderr);
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Listening on port %d\n", ntohs(serv_addr.sin_port));
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
printf("Waiting for client connection...");
|
||||
fflush(stdout);
|
||||
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
|
||||
printf("ok\n");
|
||||
fflush(stdout);
|
||||
|
||||
flags = fcntl(listenfd, F_GETFL, 0);
|
||||
fcntl(listenfd, F_SETFL, flags | O_NONBLOCK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// See if there's anything on the FIFO for us
|
||||
|
||||
void check_for_command(char *userdata)
|
||||
{
|
||||
vpiHandle systfref, args_iter, argh;
|
||||
struct t_vpi_value argval;
|
||||
struct vpi_cmd vpi;
|
||||
int nb;
|
||||
int loaded_words = 0;
|
||||
|
||||
(void)userdata;
|
||||
|
||||
// Get the command from TCP server
|
||||
if(!connfd)
|
||||
init_jtag_server(RSP_SERVER_PORT);
|
||||
nb = read(connfd, &vpi, sizeof(struct vpi_cmd));
|
||||
|
||||
if (((nb < 0) && (errno == EAGAIN)) || (nb == 0)) {
|
||||
// Nothing in the fifo this time, let's return
|
||||
return;
|
||||
} else {
|
||||
if (nb < 0) {
|
||||
// some sort of error
|
||||
perror("check_for_command");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/************* vpi.cmd to VPI ******************************/
|
||||
|
||||
// Obtain a handle to the argument list
|
||||
systfref = vpi_handle(vpiSysTfCall, NULL);
|
||||
// Now call iterate with the vpiArgument parameter
|
||||
args_iter = vpi_iterate(vpiArgument, systfref);
|
||||
// get a handle on the variable passed to the function
|
||||
argh = vpi_scan(args_iter);
|
||||
// now store the command value back in the sim
|
||||
argval.format = vpiIntVal;
|
||||
// Now set the command value
|
||||
vpi_get_value(argh, &argval);
|
||||
|
||||
argval.value.integer = (uint32_t)vpi.cmd;
|
||||
|
||||
// And vpi_put_value() it back into the sim
|
||||
vpi_put_value(argh, &argval, NULL, vpiNoDelay);
|
||||
|
||||
/************* vpi.length to VPI ******************************/
|
||||
|
||||
// now get a handle on the next object (memory array)
|
||||
argh = vpi_scan(args_iter);
|
||||
// now store the command value back in the sim
|
||||
argval.format = vpiIntVal;
|
||||
// Now set the command value
|
||||
vpi_get_value(argh, &argval);
|
||||
|
||||
argval.value.integer = (uint32_t)vpi.length;
|
||||
|
||||
// And vpi_put_value() it back into the sim
|
||||
vpi_put_value(argh, &argval, NULL, vpiNoDelay);
|
||||
|
||||
/************* vpi.nb_bits to VPI ******************************/
|
||||
|
||||
// now get a handle on the next object (memory array)
|
||||
argh = vpi_scan(args_iter);
|
||||
// now store the command value back in the sim
|
||||
argval.format = vpiIntVal;
|
||||
// Now set the command value
|
||||
vpi_get_value(argh, &argval);
|
||||
|
||||
argval.value.integer = (uint32_t)vpi.nb_bits;
|
||||
|
||||
// And vpi_put_value() it back into the sim
|
||||
vpi_put_value(argh, &argval, NULL, vpiNoDelay);
|
||||
|
||||
/*****************vpi.buffer_out to VPI ********/
|
||||
|
||||
// now get a handle on the next object (memory array)
|
||||
argh = vpi_scan(args_iter);
|
||||
vpiHandle array_word;
|
||||
|
||||
// Loop to load the words
|
||||
while (loaded_words < vpi.length) {
|
||||
// now get a handle on the current word we want in the array that was passed to us
|
||||
array_word = vpi_handle_by_index(argh, loaded_words);
|
||||
|
||||
if (array_word != NULL) {
|
||||
argval.value.integer = (uint32_t)vpi.buffer_out[loaded_words];
|
||||
// And vpi_put_value() it back into the sim
|
||||
vpi_put_value(array_word, &argval, NULL, vpiNoDelay);
|
||||
} else
|
||||
return;
|
||||
|
||||
loaded_words++;
|
||||
}
|
||||
|
||||
/*******************************************/
|
||||
|
||||
// Cleanup and return
|
||||
vpi_free_object(args_iter);
|
||||
}
|
||||
|
||||
void send_result_to_server(char *userdata)
|
||||
{
|
||||
vpiHandle systfref, args_iter, argh;
|
||||
struct t_vpi_value argval;
|
||||
ssize_t n;
|
||||
struct vpi_cmd vpi;
|
||||
|
||||
int32_t length;
|
||||
int sent_words;
|
||||
|
||||
vpiHandle array_word;
|
||||
|
||||
(void)userdata;
|
||||
|
||||
// Now setup the handles to verilog objects and check things
|
||||
// Obtain a handle to the argument list
|
||||
systfref = vpi_handle(vpiSysTfCall, NULL);
|
||||
|
||||
// Now call iterate with the vpiArgument parameter
|
||||
args_iter = vpi_iterate(vpiArgument, systfref);
|
||||
|
||||
// get a handle on the length variable
|
||||
argh = vpi_scan(args_iter);
|
||||
|
||||
argval.format = vpiIntVal;
|
||||
|
||||
// get the value for the length object
|
||||
vpi_get_value(argh, &argval);
|
||||
|
||||
// now set length
|
||||
length = argval.value.integer;
|
||||
|
||||
// now get a handle on the next object (memory array)
|
||||
argh = vpi_scan(args_iter);
|
||||
|
||||
// check we got passed a memory (array of regs)
|
||||
if (!((vpi_get(vpiType, argh) == vpiMemory)
|
||||
#ifdef MODELSIM_VPI
|
||||
|| (vpi_get(vpiType, argh) == vpiRegArray)
|
||||
#endif
|
||||
#ifdef VCS_VPI
|
||||
|| (vpi_get(vpiType, argh) == vpiRegArray)
|
||||
#endif
|
||||
)) {
|
||||
vpi_printf("jtag_vpi: ERROR: did not pass a memory to get_command_block_data\n");
|
||||
vpi_printf("jtag_vpi: ERROR: was passed type %d\n", (int)vpi_get(vpiType, argh));
|
||||
return;
|
||||
}
|
||||
|
||||
// check the memory we're writing into is big enough
|
||||
if (vpi_get(vpiSize, argh) < length ) {
|
||||
vpi_printf("jtag_vpi: ERROR: buffer passed to get_command_block_data too small. size is %d words, needs to be %d\n",
|
||||
vpi_get(vpiSize, argh), length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop to load the words
|
||||
sent_words = 0;
|
||||
while (sent_words < length) {
|
||||
// Get a handle on the current word we want in the array that was passed to us
|
||||
array_word = vpi_handle_by_index(argh, sent_words);
|
||||
|
||||
if (array_word != NULL) {
|
||||
vpi_get_value(array_word, &argval);
|
||||
vpi.buffer_in[sent_words] = (uint32_t) argval.value.integer;
|
||||
} else
|
||||
return;
|
||||
|
||||
sent_words++;
|
||||
}
|
||||
|
||||
n = write(connfd, &vpi, sizeof(struct vpi_cmd));
|
||||
if (n < (ssize_t)sizeof(struct vpi_cmd))
|
||||
vpi_printf("jtag_vpi: ERROR: error during write to server\n");
|
||||
|
||||
// Cleanup and return
|
||||
vpi_free_object(args_iter);
|
||||
}
|
||||
|
||||
void register_check_for_command(void)
|
||||
{
|
||||
s_vpi_systf_data data = {
|
||||
vpiSysTask,
|
||||
0,
|
||||
"$check_for_command",
|
||||
(void *)check_for_command,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
vpi_register_systf(&data);
|
||||
}
|
||||
|
||||
void register_send_result_to_server(void)
|
||||
{
|
||||
s_vpi_systf_data data = {
|
||||
vpiSysTask,
|
||||
0,
|
||||
"$send_result_to_server",
|
||||
(void *)send_result_to_server,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
vpi_register_systf(&data);
|
||||
}
|
||||
|
||||
void sim_reset_callback(void)
|
||||
{
|
||||
// nothing to do!
|
||||
}
|
||||
|
||||
void setup_reset_callbacks(void)
|
||||
{
|
||||
// here we setup and install callbacks for
|
||||
// the setup and management of connections to
|
||||
// the simulator upon simulation start and
|
||||
// reset
|
||||
|
||||
static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0};
|
||||
static s_vpi_value value_s = {.format = vpiBinStrVal};
|
||||
|
||||
static s_cb_data cb_data_s = {
|
||||
cbEndOfReset, // or start of simulation - initing socket fds etc
|
||||
(void *)sim_reset_callback,
|
||||
NULL,
|
||||
&time_s,
|
||||
&value_s,
|
||||
0,
|
||||
NULL
|
||||
};
|
||||
|
||||
cb_data_s.obj = NULL; /* trigger object */
|
||||
|
||||
cb_data_s.user_data = NULL;
|
||||
|
||||
// actual call to register the callback
|
||||
vpi_register_cb(&cb_data_s);
|
||||
}
|
||||
|
||||
void sim_endofcompile_callback(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void setup_endofcompile_callbacks(void)
|
||||
{
|
||||
// here we setup and install callbacks for
|
||||
// simulation finish
|
||||
|
||||
static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0};
|
||||
static s_vpi_value value_s = {.format = vpiBinStrVal};
|
||||
|
||||
static s_cb_data cb_data_s = {
|
||||
cbEndOfCompile, // end of compile
|
||||
(void *)sim_endofcompile_callback,
|
||||
NULL,
|
||||
&time_s,
|
||||
&value_s,
|
||||
0,
|
||||
NULL
|
||||
};
|
||||
|
||||
cb_data_s.obj = NULL; /* trigger object */
|
||||
|
||||
cb_data_s.user_data = NULL;
|
||||
|
||||
// actual call to register the callback
|
||||
vpi_register_cb(&cb_data_s);
|
||||
}
|
||||
|
||||
void sim_finish_callback(void)
|
||||
{
|
||||
if(connfd)
|
||||
printf("Closing RSP server\n");
|
||||
close(connfd);
|
||||
close(listenfd);
|
||||
}
|
||||
|
||||
void setup_finish_callbacks(void)
|
||||
{
|
||||
// here we setup and install callbacks for
|
||||
// simulation finish
|
||||
|
||||
static s_vpi_time time_s = {vpiScaledRealTime, 0, 0, 0};
|
||||
static s_vpi_value value_s = {.format = vpiBinStrVal};
|
||||
|
||||
static s_cb_data cb_data_s = {
|
||||
cbEndOfSimulation, // end of simulation
|
||||
(void *)sim_finish_callback,
|
||||
NULL,
|
||||
&time_s,
|
||||
&value_s,
|
||||
0,
|
||||
NULL
|
||||
};
|
||||
|
||||
cb_data_s.obj = NULL; /* trigger object */
|
||||
cb_data_s.user_data = NULL;
|
||||
|
||||
// actual call to register the callback
|
||||
vpi_register_cb(&cb_data_s);
|
||||
}
|
||||
|
||||
#ifndef VCS_VPI
|
||||
// Register the new system task here
|
||||
void (*vlog_startup_routines[])(void) = {
|
||||
#ifdef CDS_VPI
|
||||
// this installs a callback on simulator reset - something which
|
||||
// icarus does not do, so we only do it for cadence currently
|
||||
setup_reset_callbacks,
|
||||
#endif
|
||||
setup_endofcompile_callbacks,
|
||||
setup_finish_callbacks,
|
||||
register_check_for_command,
|
||||
register_send_result_to_server,
|
||||
0 // last entry must be 0
|
||||
};
|
||||
|
||||
// Entry point for testing development of the vpi functions
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@ -14,7 +14,7 @@ $(generated_dir)/%.fir $(generated_dir)/%.d: $(FIRRTL_JAR) $(chisel_srcs) $(boot
|
||||
|
||||
%.v %.conf: %.fir $(FIRRTL_JAR)
|
||||
mkdir -p $(dir $@)
|
||||
$(FIRRTL) $(patsubst %,-i %,$(filter %.fir,$^)) -o $*.v -X verilog --infer-rw $(MODEL) --repl-seq-mem -c:$(MODEL):-o:$*.conf -faf $*.anno
|
||||
$(FIRRTL) $(patsubst %,-i %,$(filter %.fir,$^)) -o $*.v -X verilog --infer-rw $(MODEL) --repl-seq-mem -c:$(MODEL):-o:$*.conf -faf $*.anno.json
|
||||
|
||||
$(generated_dir)/$(long_name).behav_srams.v : $(generated_dir)/$(long_name).conf $(VLSI_MEM_GEN)
|
||||
cd $(generated_dir) && \
|
||||
|
2
firrtl
2
firrtl
Submodule firrtl updated: b90fc784a1...6ebd1585e8
Submodule riscv-tools updated: 98682995dc...8ad8d4839a
@ -23,9 +23,6 @@ module {name}(
|
||||
reg [{output_width_minus_1}:0] rom [0:{depth_minus_1}];
|
||||
|
||||
|
||||
// 1024 is the maximum length of $readmemh filename supported by Cadence Incisive
|
||||
reg [1024 * 8 - 1:0] path;
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
`ifdef RANDOMIZE
|
||||
@ -35,10 +32,7 @@ module {name}(
|
||||
end
|
||||
`endif
|
||||
`endif
|
||||
if (!$value$plusargs("maskromhex=%s", path)) begin
|
||||
path = "{rom_hex_file}";
|
||||
end
|
||||
$readmemh(path, rom);
|
||||
$readmemh("{rom_hex_file}", rom);
|
||||
end
|
||||
|
||||
|
||||
|
@ -30,8 +30,6 @@ object DMIConsts{
|
||||
// This is used outside this block
|
||||
// to indicate 'busy'.
|
||||
def dmi_RESP_RESERVED = "b11".U
|
||||
|
||||
def dmi_haltStatusAddr = 0x40
|
||||
}
|
||||
|
||||
object DsbBusConsts {
|
||||
@ -254,7 +252,8 @@ object WNotifyWire {
|
||||
set := valid
|
||||
value := data
|
||||
Bool(true)
|
||||
}), Some(RegFieldDesc(name = name, desc = desc, access = RegFieldAccessType.WSPECIAL)))
|
||||
}), Some(RegFieldDesc(name = name, desc = desc,
|
||||
access = RegFieldAccessType.W)))
|
||||
}
|
||||
}
|
||||
|
||||
@ -328,7 +327,7 @@ class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyMod
|
||||
} .otherwise {
|
||||
when (DMCONTROLWrEn) {
|
||||
DMCONTROLNxt.ndmreset := DMCONTROLWrData.ndmreset
|
||||
DMCONTROLNxt.hartsel := DMCONTROLWrData.hartsel
|
||||
DMCONTROLNxt.hartsello := DMCONTROLWrData.hartsello
|
||||
DMCONTROLNxt.haltreq := DMCONTROLWrData.haltreq
|
||||
DMCONTROLNxt.resumereq := DMCONTROLWrData.resumereq
|
||||
DMCONTROLNxt.ackhavereset := DMCONTROLWrData.ackhavereset
|
||||
@ -375,14 +374,14 @@ class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyMod
|
||||
when (~dmactive) {
|
||||
debugIntNxt(component) := false.B
|
||||
}. otherwise {
|
||||
when (DMCONTROLWrEn && DMCONTROLWrData.hartsel === component.U) {
|
||||
when (DMCONTROLWrEn && DMCONTROLWrData.hartsello === component.U) {
|
||||
debugIntNxt(component) := DMCONTROLWrData.haltreq
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
io.innerCtrl.valid := DMCONTROLWrEn
|
||||
io.innerCtrl.bits.hartsel := DMCONTROLWrData.hartsel
|
||||
io.innerCtrl.bits.hartsel := DMCONTROLWrData.hartsello
|
||||
io.innerCtrl.bits.resumereq := DMCONTROLWrData.resumereq
|
||||
io.innerCtrl.bits.ackhavereset := DMCONTROLWrData.ackhavereset
|
||||
|
||||
@ -559,7 +558,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
HARTINFORdData.dataaddr := DsbRegAddrs.DATA.U
|
||||
HARTINFORdData.nscratch := cfg.nScratch.U
|
||||
|
||||
//----HALTSUM (and halted registers)
|
||||
//----HALTSUM*
|
||||
val numHaltedStatus = ((nComponents - 1) / 32) + 1
|
||||
val haltedStatus = Wire(Vec(numHaltedStatus, Bits(width = 32)))
|
||||
|
||||
@ -568,7 +567,12 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
}
|
||||
|
||||
val haltedSummary = Cat(haltedStatus.map(_.orR).reverse)
|
||||
val HALTSUMRdData = (new HALTSUMFields()).fromBits(haltedSummary)
|
||||
val HALTSUM1RdData = (new HALTSUM1Fields()).fromBits(haltedSummary)
|
||||
|
||||
val selectedHaltedStatus = Mux((selectedHartReg >> 5) > numHaltedStatus.U, 0.U, haltedStatus(selectedHartReg >> 5))
|
||||
val HALTSUM0RdData = (new HALTSUM0Fields()).fromBits(selectedHaltedStatus)
|
||||
|
||||
// Since we only support 1024 harts, we don't implement HALTSUM2 or HALTSUM3
|
||||
|
||||
//----ABSTRACTCS
|
||||
|
||||
@ -728,7 +732,8 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
(DMI_DMSTATUS << 2) -> Seq(RegField.r(32, DMSTATUSRdData.asUInt(), RegFieldDesc("dmi_dmstatus", ""))),
|
||||
//TODO (DMI_CFGSTRADDR0 << 2) -> cfgStrAddrFields,
|
||||
(DMI_HARTINFO << 2) -> Seq(RegField.r(32, HARTINFORdData.asUInt(), RegFieldDesc("dmi_hartinfo", "" /*, reset=Some(HARTINFORdData.litValue)*/))),
|
||||
(DMI_HALTSUM << 2) -> Seq(RegField.r(32, HALTSUMRdData.asUInt(), RegFieldDesc("dmi_haltsum", ""))),
|
||||
(DMI_HALTSUM0 << 2) -> Seq(RegField.r(32, HALTSUM0RdData.asUInt(), RegFieldDesc("dmi_haltsum0", ""))),
|
||||
(DMI_HALTSUM1 << 2) -> Seq(RegField.r(32, HALTSUM1RdData.asUInt(), RegFieldDesc("dmi_haltsum1", ""))),
|
||||
(DMI_ABSTRACTCS << 2) -> Seq(RWNotify(32, ABSTRACTCSRdData.asUInt(), ABSTRACTCSWrDataVal, ABSTRACTCSRdEn, ABSTRACTCSWrEnMaybe,
|
||||
Some(RegFieldDesc("dmi_abstractcs", "" /*, reset=Some(ABSTRACTCSReset.litValue)*/)))),
|
||||
(DMI_ABSTRACTAUTO<< 2) -> Seq(RWNotify(32, ABSTRACTAUTORdData.asUInt(), ABSTRACTAUTOWrDataVal, ABSTRACTAUTORdEn, ABSTRACTAUTOWrEnMaybe,
|
||||
@ -742,8 +747,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
(DMI_PROGBUF0 << 2) -> RegFieldGroup("dmi_progbuf", None, programBufferMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, programBufferNxt(i),
|
||||
dmiProgramBufferRdEn(i),
|
||||
dmiProgramBufferWrEnMaybe(i),
|
||||
Some(RegFieldDesc(s"dmi_progbuf_$i", "", reset = Some(0))))}),
|
||||
(DMIConsts.dmi_haltStatusAddr << 2) -> RegFieldGroup("dmi_halt_status", None, haltedStatus.zipWithIndex.map{case (x, i) => RegField.r(32, x, RegFieldDesc(s"halt_status_$i", ""))})
|
||||
Some(RegFieldDesc(s"dmi_progbuf_$i", "", reset = Some(0))))})
|
||||
)
|
||||
|
||||
abstractDataMem.zipWithIndex.foreach { case (x, i) =>
|
||||
@ -784,7 +788,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
val go = Bool()
|
||||
}
|
||||
|
||||
val flags = Wire(init = Vec.fill(nComponents){new flagBundle().fromBits(0.U)})
|
||||
val flags = Wire(init = Vec.fill(1024){new flagBundle().fromBits(0.U)})
|
||||
assert ((cfg.hartSelToHartId(selectedHartReg) < 1024.U),
|
||||
"HartSel to HartId Mapping is illegal for this Debug Implementation, because HartID must be < 1024 for it to work.");
|
||||
flags(cfg.hartSelToHartId(selectedHartReg)).go := goReg
|
||||
@ -898,15 +902,15 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
programBufferMem.zipWithIndex.map {case (x, i) => RegField(8, x, RegFieldDesc(s"debug_progbuf_$i", ""))}),
|
||||
|
||||
// These sections are read-only.
|
||||
IMPEBREAK(cfg)-> {if (cfg.hasImplicitEbreak) Seq(RegField.r(32, Instructions.EBREAK.value.U, RegFieldDesc("debug_impebreak", "Debug Implicit EBREAK"))) else Nil},
|
||||
WHERETO -> Seq(RegField.r(32, jalAbstract.asUInt, RegFieldDesc("debug_whereto", "Instruction filled in by Debug Module to control hart in Debug Mode"))),
|
||||
IMPEBREAK(cfg)-> {if (cfg.hasImplicitEbreak) Seq(RegField.r(32, Instructions.EBREAK.value.U,
|
||||
RegFieldDesc("debug_impebreak", "Debug Implicit EBREAK", reset=Some(Instructions.EBREAK.value)))) else Nil},
|
||||
WHERETO -> Seq(RegField.r(32, jalAbstract.asUInt, RegFieldDesc("debug_whereto", "Instruction filled in by Debug Module to control hart in Debug Mode", volatile = true))),
|
||||
ABSTRACT(cfg) -> RegFieldGroup("debug_abstract", Some("Instructions generated by Debug Module"),
|
||||
abstractGeneratedMem.zipWithIndex.map{ case (x,i) => RegField.r(32, x, RegFieldDesc(s"debug_abstract_$i", ""))}),
|
||||
abstractGeneratedMem.zipWithIndex.map{ case (x,i) => RegField.r(32, x, RegFieldDesc(s"debug_abstract_$i", "", volatile=true))}),
|
||||
FLAGS -> RegFieldGroup("debug_flags", Some("Memory region used to control hart going/resuming in Debug Mode"),
|
||||
flags.zipWithIndex.map{case(x, i) => RegField.r(8, x.asUInt(), RegFieldDesc(s"debug_flags_${i}", ""))}),
|
||||
flags.zipWithIndex.map{case(x, i) => RegField.r(8, x.asUInt(), RegFieldDesc(s"debug_flags_$i", "", volatile=true))}),
|
||||
ROMBASE -> RegFieldGroup("debug_rom", Some("Debug ROM"),
|
||||
DebugRomContents().zipWithIndex.map{case (x, i) => RegField.r(8, (x & 0xFF).U(8.W),
|
||||
RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
|
||||
DebugRomContents().zipWithIndex.map{case (x, i) => RegField.r(8, (x & 0xFF).U(8.W), RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
|
||||
)
|
||||
|
||||
// Override System Bus accesses with dmactive reset.
|
||||
@ -938,7 +942,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
||||
//------------------------
|
||||
// DMI Register Control and Status
|
||||
|
||||
abstractCommandBusy := (ctrlStateReg != CtrlState(Waiting))
|
||||
abstractCommandBusy := (ctrlStateReg =/= CtrlState(Waiting))
|
||||
|
||||
ABSTRACTCSWrEnLegal := (ctrlStateReg === CtrlState(Waiting))
|
||||
COMMANDWrEnLegal := (ctrlStateReg === CtrlState(Waiting))
|
||||
|
@ -172,7 +172,7 @@ class DebugTransportModuleJTAG(debugAddrBits: Int, c: JtagDTMConfig)
|
||||
// But there is actually no case in the current design where you SHOULD get an error,
|
||||
// as we haven't implemented Bus Masters or Serial Ports, which are the only cases errors
|
||||
// can occur.
|
||||
nonzeroResp := stickyNonzeroRespReg | (io.dmi.resp.valid & (io.dmi.resp.bits.resp != UInt(0)))
|
||||
nonzeroResp := stickyNonzeroRespReg | (io.dmi.resp.valid & (io.dmi.resp.bits.resp =/= UInt(0)))
|
||||
assert(!nonzeroResp, "There is no reason to get a non zero response in the current system.");
|
||||
assert(!stickyNonzeroRespReg, "There is no reason to have a sticky non zero response in the current system.");
|
||||
|
||||
|
@ -46,8 +46,8 @@ trait HasPeripheryDebugBundle {
|
||||
val dtm = Module(new SimDTM).connect(c, r, d, out)
|
||||
}
|
||||
debug.systemjtag.foreach { sj =>
|
||||
//val jtag = Module(new JTAGVPI(tckHalfPeriod = tckHalfPeriod, cmdDelay = cmdDelay)).connect(sj.jtag, sj.reset, r, out)
|
||||
val jtag = Module(new SimJTAG(tickDelay=3)).connect(sj.jtag, sj.reset, c, r, out)
|
||||
val jtag = Module(new SimJTAG(tickDelay=3)).connect(sj.jtag, c, r, ~r, out)
|
||||
sj.reset := r
|
||||
sj.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W)
|
||||
}
|
||||
debug.psd.foreach { _ <> psd }
|
||||
@ -120,15 +120,14 @@ class SimJTAG(tickDelay: Int = 50) extends BlackBox(Map("TICK_DELAY" -> IntParam
|
||||
val exit = UInt(OUTPUT, 32)
|
||||
}
|
||||
|
||||
def connect(dutio: JTAGIO, jtag_reset: Bool, tbclock: Clock, tbreset: Bool, tbsuccess: Bool) = {
|
||||
def connect(dutio: JTAGIO, tbclock: Clock, tbreset: Bool, init_done: Bool, tbsuccess: Bool) = {
|
||||
dutio <> io.jtag
|
||||
jtag_reset := tbreset
|
||||
|
||||
io.clock := tbclock
|
||||
io.reset := tbreset
|
||||
|
||||
io.enable := PlusArg("jtag_rbb_enable", 0, "Enable SimJTAG for JTAG Connections. Simulation will pause until connection is made.")
|
||||
io.init_done := ~tbreset
|
||||
io.init_done := init_done
|
||||
|
||||
// Success is determined by the gdbserver
|
||||
// which is controlling this simulation.
|
||||
@ -140,26 +139,3 @@ class SimJTAG(tickDelay: Int = 50) extends BlackBox(Map("TICK_DELAY" -> IntParam
|
||||
}
|
||||
}
|
||||
|
||||
class JTAGVPI(tckHalfPeriod: Int = 2, cmdDelay: Int = 2)(implicit val p: Parameters)
|
||||
extends BlackBox ( Map ("TCK_HALF_PERIOD" -> IntParam(tckHalfPeriod),
|
||||
"CMD_DELAY" -> IntParam(cmdDelay))) {
|
||||
val io = new Bundle {
|
||||
val jtag = new JTAGIO(hasTRSTn = false)
|
||||
val enable = Bool(INPUT)
|
||||
val init_done = Bool(INPUT)
|
||||
}
|
||||
|
||||
def connect(dutio: JTAGIO, jtag_reset: Bool, tbreset: Bool, tbsuccess: Bool) = {
|
||||
dutio <> io.jtag
|
||||
|
||||
dutio.TRSTn.foreach{ _:= false.B}
|
||||
jtag_reset := tbreset
|
||||
|
||||
io.enable := ~tbreset
|
||||
io.init_done := ~tbreset
|
||||
|
||||
// Success is determined by the gdbserver
|
||||
// which is controlling this simulation.
|
||||
tbsuccess := Bool(false)
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ class ACCESS_REGISTERFields extends Bundle {
|
||||
If \Fsize specifies a size larger than the register's actual size,
|
||||
then the access must fail. If a register is accessible, then reads of \Fsize
|
||||
less than or equal to the register's actual size must be supported.
|
||||
|
||||
This field controls the Argument Width as referenced in
|
||||
Table~\ref{tab:datareg}.
|
||||
*/
|
||||
val size = UInt(3.W)
|
||||
|
||||
|
@ -14,16 +14,33 @@ object DMI_RegAddrs {
|
||||
|
||||
Harts are nonexistent if they will never be part of this system, no
|
||||
matter how long a user waits. Eg. in a simple single-hart system only
|
||||
one hart exists, and all others are nonexistent.
|
||||
one hart exists, and all others are nonexistent. Debuggers may assume
|
||||
that a system has no harts with indexes higher than the first
|
||||
nonexistent one.
|
||||
|
||||
Harts are unavailable if they might exist/become available at a later
|
||||
time. Eg. in a multi-hart system some might temporarily be powered
|
||||
down, or a system might support hot-swapping harts.
|
||||
time, or if there are other harts with higher indexes than this one. Eg.
|
||||
in a multi-hart system some might temporarily be powered down, or a
|
||||
system might support hot-swapping harts. Systems with very large number
|
||||
of harts may permanently disable some during manufacturing, leaving
|
||||
holes in the otherwise continuous hart index space. In order to let the
|
||||
debugger discover all harts, they must show up as unavailable even if
|
||||
there is no chance of them ever becoming available.
|
||||
*/
|
||||
def DMI_DMSTATUS = 0x11
|
||||
|
||||
/* This register controls the overall debug module
|
||||
as well as the currently selected harts, as defined in \Fhasel.
|
||||
|
||||
\label{hartsel}
|
||||
\index{hartsel}
|
||||
Throughout this document we refer to \Fhartsel, which is \Fhartselhi
|
||||
combined with \Fhartsello. While the spec allows for 20 \Fhartsel bits,
|
||||
an implementation may choose to implement fewer than that. The actual
|
||||
width of \Fhartsel is called {\tt HARTSELLEN}. It must be at least 0
|
||||
and at most 20. A debugger should discover {\tt HARTSELLEN} by writing
|
||||
all ones to \Fhartsel (assuming the maximum size) and reading back the
|
||||
value to see which bits were actually set.
|
||||
*/
|
||||
def DMI_DMCONTROL = 0x10
|
||||
|
||||
@ -40,15 +57,6 @@ object DMI_RegAddrs {
|
||||
*/
|
||||
def DMI_HARTINFO = 0x12
|
||||
|
||||
/* This register contains a summary of which harts are halted.
|
||||
|
||||
Each bit contains the logical OR of 32 halt bits. When there are a
|
||||
large number of harts in the system, the debugger can first read this
|
||||
register, and then read from the halt region (0x40--0x5f) to determine
|
||||
which hart is the one that is halted.
|
||||
*/
|
||||
def DMI_HALTSUM = 0x13
|
||||
|
||||
/* This register selects which of the 32-bit portion of the hart array mask register
|
||||
is accessible in \Rhawindow.
|
||||
|
||||
@ -116,11 +124,20 @@ object DMI_RegAddrs {
|
||||
|
||||
def DMI_DEVTREEADDR3 = 0x1c
|
||||
|
||||
/* Basic read/write registers that may be read or changed by abstract
|
||||
commands.
|
||||
/* If there is more than one DM accessible on this DMI, this register
|
||||
contains the base address of the next one in the chain, or 0 if this is
|
||||
the last one in the chain.
|
||||
*/
|
||||
def DMI_NEXTDM = 0x1d
|
||||
|
||||
Accessing them while an abstract command is executing causes \Fcmderr
|
||||
to be set.
|
||||
/* \Rdatazero through \Rdataeleven are basic read/write registers that may
|
||||
be read or changed by abstract commands. \Fdatacount indicates how many
|
||||
of them are implemented, starting at \Rsbdatazero, counting up.
|
||||
Table~\ref{tab:datareg} shows how abstract commands use these
|
||||
registers.
|
||||
|
||||
Accessing these registers while an abstract command is executing causes
|
||||
\Fcmderr to be set.
|
||||
|
||||
Attempts to write them while \Fbusy is set does not change their value.
|
||||
|
||||
@ -133,11 +150,12 @@ object DMI_RegAddrs {
|
||||
|
||||
def DMI_DATA11 = 0x0f
|
||||
|
||||
/* The {\tt progbuf} registers provide read/write access to the optional
|
||||
program buffer.
|
||||
/* \Rprogbufzero through \Rprogbuffifteen provide read/write access to the
|
||||
optional program buffer. \Fprogbufsize indicates how many of them are
|
||||
implemented starting at \Rprogbufzero, counting up.
|
||||
|
||||
Accessing them while an abstract command is executing causes \Fcmderr
|
||||
to be set.
|
||||
Accessing these registers while an abstract command is executing causes
|
||||
\Fcmderr to be set.
|
||||
|
||||
Attempts to write them while \Fbusy is set does not change their value.
|
||||
*/
|
||||
@ -154,50 +172,114 @@ object DMI_RegAddrs {
|
||||
*/
|
||||
def DMI_AUTHDATA = 0x30
|
||||
|
||||
/* Each bit in this read-only register indicates whether one specific hart
|
||||
is halted or not.
|
||||
|
||||
The LSB reflects the halt status of hart \{hartsel[19:5],5'h0\}, and the
|
||||
MSB reflects halt status of hart \{hartsel[19:5],5'h1f\}.
|
||||
*/
|
||||
def DMI_HALTSUM0 = 0x40
|
||||
|
||||
/* Each bit in this read-only register indicates whether any of a group of
|
||||
harts is halted or not.
|
||||
|
||||
This register may not be present in systems with fewer than
|
||||
33 harts.
|
||||
|
||||
The LSB reflects the halt status of harts \{hartsel[19:10],10'h0\}
|
||||
through \{hartsel[19:10],10'h1f\}.
|
||||
The MSB reflects the halt status of harts \{hartsel[19:10],10'h3e0\}
|
||||
through \{hartsel[19:10],10'h3ff\}.
|
||||
*/
|
||||
def DMI_HALTSUM1 = 0x13
|
||||
|
||||
/* Each bit in this read-only register indicates whether any of a group of
|
||||
harts is halted or not.
|
||||
|
||||
This register may not be present in systems with fewer than
|
||||
1025 harts.
|
||||
|
||||
The LSB reflects the halt status of harts \{hartsel[19:15],15'h0\}
|
||||
through \{hartsel[19:15],15'h3ff\}.
|
||||
The MSB reflects the halt status of harts \{hartsel[19:15],15'h7c00\}
|
||||
through \{hartsel[19:15],15'h7fff\}.
|
||||
*/
|
||||
def DMI_HALTSUM2 = 0x34
|
||||
|
||||
/* Each bit in this read-only register indicates whether any of a group of
|
||||
harts is halted or not.
|
||||
|
||||
This register may not be present in systems with fewer than
|
||||
32769 harts.
|
||||
|
||||
The LSB reflects the halt status of harts 20'h0 through 20'h7fff.
|
||||
The MSB reflects the halt status of harts 20'hf8000 through 20'hfffff.
|
||||
*/
|
||||
def DMI_HALTSUM3 = 0x35
|
||||
|
||||
/* If \Fsbasize is less than 97, then this register is not present.
|
||||
|
||||
When the system bus master is busy, writes to this register will set
|
||||
\Fsbbusyerror and don't do anything else.
|
||||
*/
|
||||
def DMI_SBADDRESS3 = 0x37
|
||||
|
||||
def DMI_SBCS = 0x38
|
||||
|
||||
/* If \Fsbasize is 0, then this register is not present.
|
||||
|
||||
When the system bus master is busy,
|
||||
writes to this register will set \Fsberror.
|
||||
When the system bus master is busy, writes to this register will set
|
||||
\Fsbbusyerror and don't do anything else.
|
||||
|
||||
If \Fsberror is 0 and \Fsbautoread is set then the system bus
|
||||
master will start
|
||||
to read after updating the address from \Faddress. The access size is
|
||||
controlled by \Fsbaccess in \Rsbcs.
|
||||
|
||||
If \Fsbsingleread is set, the bit is cleared.
|
||||
\begin{steps}{If \Fsberror is 0, \Fsbbusyerror is 0, and \Fsbreadonaddr
|
||||
is set then writes to this register start the following:}
|
||||
\item Set \Fsbbusy.
|
||||
\item Perform a bus read from the new value of {\tt sbaddress}.
|
||||
\item If the read succeeded and \Fsbautoincrement is set, increment
|
||||
{\tt sbaddress}.
|
||||
\item Clear \Fsbbusy.
|
||||
\end{steps}
|
||||
*/
|
||||
def DMI_SBADDRESS0 = 0x39
|
||||
|
||||
/* If \Fsbasize is less than 33, then this register is not present.
|
||||
|
||||
When the system bus master is busy, writes to this register will set
|
||||
\Fsbbusyerror and don't do anything else.
|
||||
*/
|
||||
def DMI_SBADDRESS1 = 0x3a
|
||||
|
||||
/* If \Fsbasize is less than 65, then this register is not present.
|
||||
|
||||
When the system bus master is busy, writes to this register will set
|
||||
\Fsbbusyerror and don't do anything else.
|
||||
*/
|
||||
def DMI_SBADDRESS2 = 0x3b
|
||||
|
||||
/* If all of the {\tt sbaccess} bits in \Rsbcs are 0, then this register
|
||||
is not present.
|
||||
|
||||
Any successful system bus read updates the data in this register, and
|
||||
marks it no longer stale.
|
||||
Any successful system bus read updates the data in this register.
|
||||
|
||||
If \Fsberror isn't 0 then accesses do nothing.
|
||||
If \Fsberror or \Fsbbusyerror both aren't 0 then accesses do nothing.
|
||||
|
||||
\begin{steps}{Writes to this register:}
|
||||
\item If the bus master is busy then accesses set \Fsberror, and
|
||||
don't do anything else.
|
||||
\item Start a bus write of {\tt sbdata} to {\tt sbaddress}.
|
||||
\item If \Fsbautoincrement is set, increment {\tt sbaddress}.
|
||||
If the bus master is busy then accesses set \Fsbbusyerror, and don't do
|
||||
anything else.
|
||||
|
||||
\begin{steps}{Writes to this register start the following:}
|
||||
\item Set \Fsbbusy.
|
||||
\item Perform a bus write of the new value of {\tt sbdata} to {\tt sbaddress}.
|
||||
\item If the write succeeded and \Fsbautoincrement is set,
|
||||
increment {\tt sbaddress}.
|
||||
\item Clear \Fsbbusy.
|
||||
\end{steps}
|
||||
|
||||
\begin{steps}{Reads from this register:}
|
||||
\item If the register is marked stale, then set \Fsberror and don't
|
||||
do anything else.
|
||||
\begin{steps}{Reads from this register start the following:}
|
||||
\item ``Return'' the data.
|
||||
\item Mark the register stale.
|
||||
\item Set \Fsbbusy.
|
||||
\item If \Fsbautoincrement is set, increment {\tt sbaddress}.
|
||||
\item If \Fsbautoread is set, start another system bus read.
|
||||
\item If \Fsbreadondata is set, perform another system bus read.
|
||||
\item Clear \Fsbbusy.
|
||||
\end{steps}
|
||||
|
||||
Only \Rsbdatazero has this behavior. The other {\tt sbdata} registers
|
||||
@ -209,14 +291,23 @@ object DMI_RegAddrs {
|
||||
|
||||
/* If \Fsbaccesssixtyfour and \Fsbaccessonetwentyeight are 0, then this
|
||||
register is not present.
|
||||
|
||||
If the bus master is busy then accesses set \Fsbbusyerror, and don't do
|
||||
anything else.
|
||||
*/
|
||||
def DMI_SBDATA1 = 0x3d
|
||||
|
||||
/* This register only exists if \Fsbaccessonetwentyeight is 1.
|
||||
|
||||
If the bus master is busy then accesses set \Fsbbusyerror, and don't do
|
||||
anything else.
|
||||
*/
|
||||
def DMI_SBDATA2 = 0x3e
|
||||
|
||||
/* This register only exists if \Fsbaccessonetwentyeight is 1.
|
||||
|
||||
If the bus master is busy then accesses set \Fsbbusyerror, and don't do
|
||||
anything else.
|
||||
*/
|
||||
def DMI_SBDATA3 = 0x3f
|
||||
|
||||
@ -224,20 +315,7 @@ object DMI_RegAddrs {
|
||||
|
||||
class DMSTATUSFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(5.W)
|
||||
|
||||
/* Gets set if the Debug Module was accessed incorrectly.
|
||||
|
||||
0 (none): No error.
|
||||
|
||||
1 (badaddr): There was an access to an unimplemented Debug Module
|
||||
address.
|
||||
|
||||
7 (other): An access failed for another reason.
|
||||
*/
|
||||
val dmerr = UInt(3.W)
|
||||
|
||||
val reserved1 = UInt(1.W)
|
||||
val reserved0 = UInt(9.W)
|
||||
|
||||
/* If 1, then there is an implicit {\tt ebreak} instruction at the
|
||||
non-existent word immediately after the Program Buffer. This saves
|
||||
@ -248,7 +326,7 @@ class DMSTATUSFields extends Bundle {
|
||||
*/
|
||||
val impebreak = Bool()
|
||||
|
||||
val reserved2 = UInt(2.W)
|
||||
val reserved1 = UInt(2.W)
|
||||
|
||||
/* This field is 1 when all currently selected harts have been reset but the reset has not been acknowledged.
|
||||
*/
|
||||
@ -317,7 +395,7 @@ class DMSTATUSFields extends Bundle {
|
||||
*/
|
||||
val authbusy = Bool()
|
||||
|
||||
val reserved3 = UInt(1.W)
|
||||
val reserved2 = UInt(1.W)
|
||||
|
||||
/* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
|
||||
is not relevant to the Device Tree.
|
||||
@ -401,12 +479,17 @@ class DMCONTROLFields extends Bundle {
|
||||
*/
|
||||
val hasel = Bool()
|
||||
|
||||
/* The DM-specific index of the hart to select. This hart is always part of the
|
||||
currently selected harts.
|
||||
/* The low 10 bits of \Fhartsel: the DM-specific index of the hart to
|
||||
select. This hart is always part of the currently selected harts.
|
||||
*/
|
||||
val hartsel = UInt(10.W)
|
||||
val hartsello = UInt(10.W)
|
||||
|
||||
val reserved1 = UInt(14.W)
|
||||
/* The high 10 bits of \Fhartsel: the DM-specific index of the hart to
|
||||
select. This hart is always part of the currently selected harts.
|
||||
*/
|
||||
val hartselhi = UInt(10.W)
|
||||
|
||||
val reserved1 = UInt(4.W)
|
||||
|
||||
/* This bit controls the reset signal from the DM to the rest of the
|
||||
system. The signal should reset every part of the system, including
|
||||
@ -468,6 +551,9 @@ class HARTINFOFields extends Bundle {
|
||||
|
||||
If \Fdataaccess is 1: Number of 32-bit words in the memory map
|
||||
dedicated to shadowing the {\tt data} registers.
|
||||
|
||||
Since there are at most 12 {\tt data} registers, the value in this
|
||||
register must be 12 or smaller.
|
||||
*/
|
||||
val datasize = UInt(4.W)
|
||||
|
||||
@ -481,79 +567,11 @@ class HARTINFOFields extends Bundle {
|
||||
|
||||
}
|
||||
|
||||
class HALTSUMFields extends Bundle {
|
||||
|
||||
val halt1023_992 = Bool()
|
||||
|
||||
val halt991_960 = Bool()
|
||||
|
||||
val halt959_928 = Bool()
|
||||
|
||||
val halt927_896 = Bool()
|
||||
|
||||
val halt895_864 = Bool()
|
||||
|
||||
val halt863_832 = Bool()
|
||||
|
||||
val halt831_800 = Bool()
|
||||
|
||||
val halt799_768 = Bool()
|
||||
|
||||
val halt767_736 = Bool()
|
||||
|
||||
val halt735_704 = Bool()
|
||||
|
||||
val halt703_672 = Bool()
|
||||
|
||||
val halt671_640 = Bool()
|
||||
|
||||
val halt639_608 = Bool()
|
||||
|
||||
val halt607_576 = Bool()
|
||||
|
||||
val halt575_544 = Bool()
|
||||
|
||||
val halt543_512 = Bool()
|
||||
|
||||
val halt511_480 = Bool()
|
||||
|
||||
val halt479_448 = Bool()
|
||||
|
||||
val halt447_416 = Bool()
|
||||
|
||||
val halt415_384 = Bool()
|
||||
|
||||
val halt383_352 = Bool()
|
||||
|
||||
val halt351_320 = Bool()
|
||||
|
||||
val halt319_288 = Bool()
|
||||
|
||||
val halt287_256 = Bool()
|
||||
|
||||
val halt255_224 = Bool()
|
||||
|
||||
val halt223_192 = Bool()
|
||||
|
||||
val halt191_160 = Bool()
|
||||
|
||||
val halt159_128 = Bool()
|
||||
|
||||
val halt127_96 = Bool()
|
||||
|
||||
val halt95_64 = Bool()
|
||||
|
||||
val halt63_32 = Bool()
|
||||
|
||||
val halt31_0 = Bool()
|
||||
|
||||
}
|
||||
|
||||
class HAWINDOWSELFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(27.W)
|
||||
val reserved0 = UInt(17.W)
|
||||
|
||||
val hawindowsel = UInt(5.W)
|
||||
val hawindowsel = UInt(15.W)
|
||||
|
||||
}
|
||||
|
||||
@ -606,12 +624,12 @@ class ABSTRACTCSFields extends Bundle {
|
||||
*/
|
||||
val cmderr = UInt(3.W)
|
||||
|
||||
val reserved3 = UInt(3.W)
|
||||
val reserved3 = UInt(4.W)
|
||||
|
||||
/* Number of {\tt data} registers that are implemented as part of the
|
||||
abstract command interface. Valid sizes are 0 - 12.
|
||||
*/
|
||||
val datacount = UInt(5.W)
|
||||
val datacount = UInt(4.W)
|
||||
|
||||
}
|
||||
|
||||
@ -631,14 +649,14 @@ class COMMANDFields extends Bundle {
|
||||
|
||||
class ABSTRACTAUTOFields extends Bundle {
|
||||
|
||||
/* When a bit in this field is 1, read or write accesses the corresponding {\tt progbuf} word
|
||||
/* When a bit in this field is 1, read or write accesses to the corresponding {\tt progbuf} word
|
||||
cause the command in \Rcommand to be executed again.
|
||||
*/
|
||||
val autoexecprogbuf = UInt(16.W)
|
||||
|
||||
val reserved0 = UInt(4.W)
|
||||
|
||||
/* When a bit in this field is 1, read or write accesses the corresponding {\tt data} word
|
||||
/* When a bit in this field is 1, read or write accesses to the corresponding {\tt data} word
|
||||
cause the command in \Rcommand to be executed again.
|
||||
*/
|
||||
val autoexecdata = UInt(12.W)
|
||||
@ -651,6 +669,12 @@ class DEVTREEADDR0Fields extends Bundle {
|
||||
|
||||
}
|
||||
|
||||
class NEXTDMFields extends Bundle {
|
||||
|
||||
val addr = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class DATA0Fields extends Bundle {
|
||||
|
||||
val data = UInt(32.W)
|
||||
@ -669,17 +693,78 @@ class AUTHDATAFields extends Bundle {
|
||||
|
||||
}
|
||||
|
||||
class HALTSUM0Fields extends Bundle {
|
||||
|
||||
val haltsum0 = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class HALTSUM1Fields extends Bundle {
|
||||
|
||||
val haltsum1 = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class HALTSUM2Fields extends Bundle {
|
||||
|
||||
val haltsum2 = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class HALTSUM3Fields extends Bundle {
|
||||
|
||||
val haltsum3 = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBADDRESS3Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 127:96 of the physical address in {\tt sbaddress} (if
|
||||
the system address bus is that wide).
|
||||
*/
|
||||
val address = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBCSFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(11.W)
|
||||
/* 0: The System Bus interface conforms to mainline drafts of this
|
||||
spec older than 1 January, 2018.
|
||||
|
||||
/* When a 1 is written here, triggers a read at the address in {\tt
|
||||
sbaddress} using the access size set by \Fsbaccess.
|
||||
1: The System Bus interface conforms to this version of the spec.
|
||||
|
||||
Other values are reserved for future versions.
|
||||
*/
|
||||
val sbsingleread = Bool()
|
||||
val sbversion = UInt(3.W)
|
||||
|
||||
/* Select the access size to use for system bus accesses triggered by
|
||||
writes to the {\tt sbaddress} registers or \Rsbdatazero.
|
||||
val reserved0 = UInt(6.W)
|
||||
|
||||
/* Set when the debugger attempts to read data while a read is in
|
||||
progress, or when the debugger initiates a new access while one is
|
||||
already in progress (while \Fsbbusy is set). It remains set until
|
||||
it's explicitly cleared by the debugger.
|
||||
|
||||
While this field is non-zero, no more system bus accesses can be
|
||||
initiated by the debug module.
|
||||
*/
|
||||
val sbbusyerror = Bool()
|
||||
|
||||
/* When 1, indicates the system bus master is busy. (Whether the
|
||||
system bus itself is busy is related, but not the same thing.) This
|
||||
bit goes high immediately when a read or write is requested for any
|
||||
reason, and does not go low until the access is fully completed.
|
||||
|
||||
To avoid race conditions, debuggers must not try to clear \Fsberror
|
||||
until they read \Fsbbusy as 0.
|
||||
*/
|
||||
val sbbusy = Bool()
|
||||
|
||||
/* When 1, every write to \Rsbaddresszero automatically triggers a
|
||||
system bus read at the new address.
|
||||
*/
|
||||
val sbreadonaddr = Bool()
|
||||
|
||||
/* Select the access size to use for system bus accesses.
|
||||
|
||||
0: 8-bit
|
||||
|
||||
@ -691,8 +776,8 @@ class SBCSFields extends Bundle {
|
||||
|
||||
4: 128-bit
|
||||
|
||||
If an unsupported system bus access size is written here, the DM
|
||||
does not perform the access and sberror is set to 3.
|
||||
If \Fsbaccess has an unsupported value when the DM starts a bus
|
||||
access, the access is not performed and \Fsberror is set to 3.
|
||||
*/
|
||||
val sbaccess = UInt(3.W)
|
||||
|
||||
@ -704,7 +789,7 @@ class SBCSFields extends Bundle {
|
||||
/* When 1, every read from \Rsbdatazero automatically triggers a
|
||||
system bus read at the (possibly auto-incremented) address.
|
||||
*/
|
||||
val sbautoread = Bool()
|
||||
val sbreadondata = Bool()
|
||||
|
||||
/* When the debug module's system bus
|
||||
master causes a bus error, this field gets set. The bits in this
|
||||
@ -719,10 +804,6 @@ class SBCSFields extends Bundle {
|
||||
2: A bad address was accessed.
|
||||
|
||||
3: There was some other error (eg. alignment).
|
||||
|
||||
4: The system bus master was busy when one of the
|
||||
{\tt sbaddress} or {\tt sbdata} registers was written,
|
||||
or \Rsbdatazero was read when it had stale data.
|
||||
*/
|
||||
val sberror = UInt(3.W)
|
||||
|
||||
|
@ -103,7 +103,7 @@ class TLBusBypassBar(implicit p: Parameters) extends LazyModule
|
||||
flight := next_flight
|
||||
|
||||
when (next_flight === UInt(0)) { bypass := io.bypass }
|
||||
val stall = (bypass != io.bypass) && a_first
|
||||
val stall = (bypass =/= io.bypass) && a_first
|
||||
|
||||
out0.a.valid := !stall && in.a.valid && bypass
|
||||
out1.a.valid := !stall && in.a.valid && !bypass
|
||||
|
@ -82,10 +82,12 @@ class CLINT(params: CLINTParams, beatBytes: Int)(implicit p: Parameters) extends
|
||||
*/
|
||||
|
||||
node.regmap(
|
||||
0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) => RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0)))}),
|
||||
timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) =>
|
||||
RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"), RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))},
|
||||
timeOffset -> RegFieldGroup("mtime", Some("Timer Register"), RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0)))))
|
||||
0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) =>
|
||||
RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0)))}),
|
||||
timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) => RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"),
|
||||
RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))},
|
||||
timeOffset -> RegFieldGroup("mtime", Some("Timer Register"),
|
||||
RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0), volatile=true))))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -168,10 +168,23 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
|
||||
harts(hart) := ShiftRegister(Reg(next = maxPri) > Cat(UInt(1), threshold(hart)), params.intStages)
|
||||
}
|
||||
|
||||
def priorityRegDesc(i: Int) = RegFieldDesc(s"priority_$i", s"Acting priority of interrupt source $i", reset=if (nPriorities > 0) None else Some(1))
|
||||
def pendingRegDesc(i: Int) = RegFieldDesc(s"pending_$i", s"Set to 1 if interrupt source $i is pending, regardless of its enable or priority setting.")
|
||||
def priorityRegDesc(i: Int) = if (i > 0) {
|
||||
RegFieldDesc(s"priority_$i", s"Acting priority of interrupt source $i",
|
||||
reset=if (nPriorities > 0) None else Some(1),
|
||||
wrType=Some(RegFieldWrType.MODIFY))
|
||||
} else {
|
||||
RegFieldDesc.reserved
|
||||
}
|
||||
def pendingRegDesc(i: Int) = if (i > 0) {
|
||||
RegFieldDesc(s"pending_$i", s"Set to 1 if interrupt source $i is pending, regardless of its enable or priority setting.",
|
||||
volatile = true)
|
||||
} else {
|
||||
RegFieldDesc.reserved
|
||||
}
|
||||
|
||||
def priorityRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, priorityRegDesc(i)) else RegField.r(32, x, priorityRegDesc(i))
|
||||
val priorityRegFields = Seq(PLICConsts.priorityBase -> RegFieldGroup("priority", Some("Acting priorities of each interrupt source. 32 bits for each interrupt source."),
|
||||
val priorityRegFields = Seq(PLICConsts.priorityBase -> RegFieldGroup("priority",
|
||||
Some(s"Acting priorities of each interrupt source. Maximum legal value is ${nPriorities}. 32 bits for each interrupt source."),
|
||||
priority.zipWithIndex.map{case (p, i) => priorityRegField(p, i)}))
|
||||
val pendingRegFields = Seq(PLICConsts.pendingBase -> RegFieldGroup("pending", Some("Pending Bit Array. 1 Bit for each interrupt source."),
|
||||
pending.zipWithIndex.map{case (b, i) => RegField.r(1, b, pendingRegDesc(i))}))
|
||||
@ -179,7 +192,11 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
|
||||
|
||||
val enableRegFields = enables.zipWithIndex.map { case (e, i) =>
|
||||
PLICConsts.enableBase(i) -> RegFieldGroup(s"enables_${i}", Some(s"Enable bits for each interrupt source for target $i. 1 bit for each interrupt source."),
|
||||
e.zipWithIndex.map{case (b, j) => RegField(1, b, RegFieldDesc(s"enable_${i}_${j}", s"Enable interrupt for source $j for target $i.", reset=None))})
|
||||
e.zipWithIndex.map{case (b, j) => if (j > 0) {
|
||||
RegField(1, b, RegFieldDesc(s"enable_${i}_${j}", s"Enable interrupt for source $j for target $i.", reset=None))
|
||||
} else {
|
||||
RegField(1, b, RegFieldDesc.reserved)
|
||||
}})
|
||||
}
|
||||
|
||||
// When a hart reads a claim/complete register, then the
|
||||
@ -213,7 +230,9 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
|
||||
g.complete := c
|
||||
}
|
||||
|
||||
def thresholdRegDesc(i: Int) = RegFieldDesc(s"threshold_$i", s"Interrupt & claim threshold for target $i", reset=if (nPriorities > 0) None else Some(1))
|
||||
def thresholdRegDesc(i: Int) = RegFieldDesc(s"threshold_$i", s"Interrupt & claim threshold for target $i. Maximum value is ${nPriorities}.",
|
||||
reset=if (nPriorities > 0) None else Some(1),
|
||||
wrType=Some(RegFieldWrType.MODIFY))
|
||||
def thresholdRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, thresholdRegDesc(i)) else RegField.r(32, x, thresholdRegDesc(i))
|
||||
|
||||
val hartRegFields = Seq.tabulate(nHarts) { i =>
|
||||
@ -235,7 +254,9 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
|
||||
s"Claim/Complete register for Target $i. Reading this register returns the claimed interrupt number and makes it no longer pending." +
|
||||
s"Writing the interrupt number back completes the interrupt.",
|
||||
reset = None,
|
||||
access = RegFieldAccessType.RWSPECIAL))
|
||||
wrType = Some(RegFieldWrType.MODIFY),
|
||||
rdAction = Some(RegFieldRdAction.MODIFY),
|
||||
volatile = true))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
75
src/main/scala/diplomacy/AddressRange.scala
Normal file
75
src/main/scala/diplomacy/AddressRange.scala
Normal file
@ -0,0 +1,75 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.diplomacy
|
||||
|
||||
import Chisel._
|
||||
|
||||
// Use AddressSet instead -- this is just for pretty printing
|
||||
case class AddressRange(base: BigInt, size: BigInt) extends Ordered[AddressRange]
|
||||
{
|
||||
val end = base + size
|
||||
|
||||
require (base >= 0, s"AddressRange base must be positive, got: $base")
|
||||
require (size > 0, s"AddressRange size must be > 0, got: $size")
|
||||
|
||||
def compare(x: AddressRange) = {
|
||||
val primary = (this.base - x.base).signum
|
||||
val secondary = (x.size - this.size).signum
|
||||
if (primary != 0) primary else secondary
|
||||
}
|
||||
|
||||
def contains(x: AddressRange) = base <= x.base && x.end <= end
|
||||
def union(x: AddressRange): Option[AddressRange] = {
|
||||
if (base > x.end || x.base > end) {
|
||||
None
|
||||
} else {
|
||||
val obase = if (base < x.base) base else x.base
|
||||
val oend = if (end > x.end) end else x.end
|
||||
Some(AddressRange(obase, oend-obase))
|
||||
}
|
||||
}
|
||||
|
||||
private def helper(base: BigInt, end: BigInt) =
|
||||
if (base < end) Seq(AddressRange(base, end-base)) else Nil
|
||||
def subtract(x: AddressRange) =
|
||||
helper(base, end min x.base) ++ helper(base max x.end, end)
|
||||
|
||||
// We always want to see things in hex
|
||||
override def toString() = "AddressRange(0x%x, 0x%x)".format(base, size)
|
||||
}
|
||||
|
||||
object AddressRange
|
||||
{
|
||||
def fromSets(seq: Seq[AddressSet]): Seq[AddressRange] = unify(seq.flatMap(_.toRanges))
|
||||
def unify(seq: Seq[AddressRange]): Seq[AddressRange] = {
|
||||
if (seq.isEmpty) return Nil
|
||||
val ranges = seq.sorted
|
||||
ranges.tail.foldLeft(Seq(ranges.head)) { case (head :: tail, x) =>
|
||||
head.union(x) match {
|
||||
case Some(z) => z :: tail
|
||||
case None => x :: head :: tail
|
||||
}
|
||||
}.reverse
|
||||
}
|
||||
// Set subtraction... O(n*n) b/c I am lazy
|
||||
def subtract(from: Seq[AddressRange], take: Seq[AddressRange]): Seq[AddressRange] =
|
||||
take.foldLeft(from) { case (left, r) => left.flatMap { _.subtract(r) } }
|
||||
}
|
||||
|
||||
case class AddressMapEntry(range: AddressRange, permissions: ResourcePermissions, names: Seq[String]) {
|
||||
val ResourcePermissions(r, w, x, c, a) = permissions
|
||||
|
||||
def toString(aw: Int) = s"\t%${aw}x - %${aw}x %c%c%c%c%c %s".format(
|
||||
range.base,
|
||||
range.base+range.size,
|
||||
if (a) 'A' else ' ',
|
||||
if (r) 'R' else ' ',
|
||||
if (w) 'W' else ' ',
|
||||
if (x) 'X' else ' ',
|
||||
if (c) 'C' else ' ',
|
||||
names.mkString(", "))
|
||||
|
||||
def serialize = s"""{"base":[${range.base}],"size":[${range.size}],""" +
|
||||
s""""r":[$r],"w":[$w],"x":[$x],"c":[$c],"a":[$a],""" +
|
||||
s""""names":[${names.map('"'+_+'"').mkString(",")}]}"""
|
||||
}
|
@ -104,40 +104,6 @@ object TransferSizes {
|
||||
implicit def asBool(x: TransferSizes) = !x.none
|
||||
}
|
||||
|
||||
// Use AddressSet instead -- this is just for pretty printing
|
||||
case class AddressRange(base: BigInt, size: BigInt) extends Ordered[AddressRange]
|
||||
{
|
||||
val end = base + size
|
||||
|
||||
require (base >= 0, s"AddressRange base must be positive, got: $base")
|
||||
require (size > 0, s"AddressRange size must be > 0, got: $size")
|
||||
|
||||
def compare(x: AddressRange) = {
|
||||
val primary = (this.base - x.base).signum
|
||||
val secondary = (x.size - this.size).signum
|
||||
if (primary != 0) primary else secondary
|
||||
}
|
||||
|
||||
def contains(x: AddressRange) = base <= x.base && x.end <= end
|
||||
def union(x: AddressRange): Option[AddressRange] = {
|
||||
if (base > x.end || x.base > end) {
|
||||
None
|
||||
} else {
|
||||
val obase = if (base < x.base) base else x.base
|
||||
val oend = if (end > x.end) end else x.end
|
||||
Some(AddressRange(obase, oend-obase))
|
||||
}
|
||||
}
|
||||
|
||||
private def helper(base: BigInt, end: BigInt) =
|
||||
if (base < end) Seq(AddressRange(base, end-base)) else Nil
|
||||
def subtract(x: AddressRange) =
|
||||
helper(base, end min x.base) ++ helper(base max x.end, end)
|
||||
|
||||
// We always want to see things in hex
|
||||
override def toString() = "AddressRange(0x%x, 0x%x)".format(base, size)
|
||||
}
|
||||
|
||||
// AddressSets specify the address space managed by the manager
|
||||
// Base is the base address, and mask are the bits consumed by the manager
|
||||
// e.g: base=0x200, mask=0xff describes a device managing 0x200-0x2ff
|
||||
@ -210,24 +176,6 @@ case class AddressSet(base: BigInt, mask: BigInt) extends Ordered[AddressSet]
|
||||
}
|
||||
}
|
||||
|
||||
object AddressRange
|
||||
{
|
||||
def fromSets(seq: Seq[AddressSet]): Seq[AddressRange] = unify(seq.flatMap(_.toRanges))
|
||||
def unify(seq: Seq[AddressRange]): Seq[AddressRange] = {
|
||||
if (seq.isEmpty) return Nil
|
||||
val ranges = seq.sorted
|
||||
ranges.tail.foldLeft(Seq(ranges.head)) { case (head :: tail, x) =>
|
||||
head.union(x) match {
|
||||
case Some(z) => z :: tail
|
||||
case None => x :: head :: tail
|
||||
}
|
||||
}.reverse
|
||||
}
|
||||
// Set subtraction... O(n*n) b/c I am lazy
|
||||
def subtract(from: Seq[AddressRange], take: Seq[AddressRange]): Seq[AddressRange] =
|
||||
take.foldLeft(from) { case (left, r) => left.flatMap { _.subtract(r) } }
|
||||
}
|
||||
|
||||
object AddressSet
|
||||
{
|
||||
val everything = AddressSet(0, -1)
|
||||
|
@ -251,6 +251,15 @@ trait BindingScope
|
||||
}
|
||||
}
|
||||
|
||||
private def collect(path: List[String], value: ResourceValue): List[(String, ResourceAddress)] = {
|
||||
value match {
|
||||
case r: ResourceAddress => List((path(1), r))
|
||||
case b: ResourceMapping => List((path(1), ResourceAddress(b.address, b.permissions)))
|
||||
case ResourceMap(value, _) => value.toList.flatMap { case (key, seq) => seq.flatMap(r => collect(key :: path, r)) }
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate the device tree. */
|
||||
def bindingTree: ResourceMap = {
|
||||
eval
|
||||
@ -263,6 +272,9 @@ trait BindingScope
|
||||
expand(tokens, Seq(ResourceMap(mapping, Seq(d.label)))) })
|
||||
ResourceMap(SortedMap("/" -> tree))
|
||||
}
|
||||
|
||||
/** Collect resource addresses from tree. */
|
||||
def collectResourceAddresses = collect(Nil, bindingTree)
|
||||
}
|
||||
|
||||
object BindingScope
|
||||
|
@ -22,10 +22,9 @@ abstract class DiplomaticSRAM(
|
||||
def mask: List[Boolean] = bigBits(address.mask >> log2Ceil(beatBytes))
|
||||
|
||||
// Use single-ported memory with byte-write enable
|
||||
def makeSinglePortedByteWriteSeqMem(size: Int) = {
|
||||
def makeSinglePortedByteWriteSeqMem(size: Int, lanes: Int = beatBytes, bits: Int = 8) = {
|
||||
// We require the address range to include an entire beat (for the write mask)
|
||||
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||
val mem = SeqMem(size, Vec(beatBytes, Bits(width = 8)))
|
||||
val mem = SeqMem(size, Vec(lanes, Bits(width = bits)))
|
||||
devName.foreach(n => mem.suggestName(n.split("-").last))
|
||||
mem
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class GroundTestSubsystem(implicit p: Parameters) extends BaseSubsystem
|
||||
)}
|
||||
|
||||
tiles.flatMap(_.dcacheOpt).foreach { dc =>
|
||||
sbus.fromTile(None, buffers = 1){ dc.node }
|
||||
sbus.fromTile(None, buffer = BufferParams.default){ dc.node }
|
||||
}
|
||||
|
||||
// No PLIC in ground test; so just sink the interrupts to nowhere
|
||||
|
23
src/main/scala/regmapper/Annotation.scala
Normal file
23
src/main/scala/regmapper/Annotation.scala
Normal file
@ -0,0 +1,23 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.regmapper
|
||||
|
||||
import org.json4s.JsonDSL._
|
||||
import org.json4s.jackson.JsonMethods.{pretty, render}
|
||||
|
||||
object RegMappingAnnotation {
|
||||
def serialize(base: BigInt, name: String, mapping: RegField.Map*): String = {
|
||||
val regDescs = mapping.flatMap { case (byte, seq) =>
|
||||
seq.map(_.width).scanLeft(0)(_ + _).zip(seq).map { case (bit, f) =>
|
||||
val anonName = s"unnamedRegField${byte.toHexString}_${bit}"
|
||||
(f.desc.map{ _.name}.getOrElse(anonName)) -> f.toJson(byte, bit)
|
||||
}
|
||||
}
|
||||
|
||||
pretty(render(
|
||||
("peripheral" -> (
|
||||
("displayName" -> name) ~
|
||||
("baseAddress" -> s"0x${base.toInt.toHexString}") ~
|
||||
("regfields" -> regDescs)))))
|
||||
}
|
||||
}
|
47
src/main/scala/regmapper/DescribedReg.scala
Normal file
47
src/main/scala/regmapper/DescribedReg.scala
Normal file
@ -0,0 +1,47 @@
|
||||
// See LICENSE for license details.
|
||||
package freechips.rocketchip.regmapper
|
||||
|
||||
import Chisel._
|
||||
import chisel3.experimental._
|
||||
import chisel3.{Input, Output}
|
||||
import freechips.rocketchip.util.{AsyncResetRegVec, SimpleRegIO}
|
||||
|
||||
object DescribedReg {
|
||||
import freechips.rocketchip.regmapper.RegFieldAccessType._
|
||||
import freechips.rocketchip.regmapper.RegFieldWrType._
|
||||
import freechips.rocketchip.regmapper.RegFieldRdAction._
|
||||
|
||||
def apply[T <: Data](
|
||||
gen: => T,
|
||||
name: String,
|
||||
desc: String,
|
||||
reset: Option[T],
|
||||
access: RegFieldAccessType = RW,
|
||||
wrType: Option[RegFieldWrType] = None,
|
||||
rdAction: Option[RegFieldRdAction] = None,
|
||||
volatile: Boolean = false,
|
||||
enumerations: Map[BigInt, (String, String)] = Map()): (T, RegFieldDesc) = {
|
||||
val rdesc = RegFieldDesc(name, desc, None, None,
|
||||
access, wrType, rdAction, volatile, reset.map{_.litValue}, enumerations)
|
||||
val reg = reset.map{i => RegInit(i)}.getOrElse(Reg(gen))
|
||||
reg.suggestName(name + "_reg")
|
||||
(reg, rdesc)
|
||||
}
|
||||
|
||||
def async(
|
||||
width: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
reset: Int,
|
||||
access: RegFieldAccessType = RW,
|
||||
wrType: Option[RegFieldWrType] = None,
|
||||
rdAction: Option[RegFieldRdAction] = None,
|
||||
volatile: Boolean = false,
|
||||
enumerations: Map[BigInt, (String, String)] = Map()): (SimpleRegIO, RegFieldDesc) = {
|
||||
val rdesc = RegFieldDesc(name, desc, None, None,
|
||||
access, wrType, rdAction, volatile, Some(reset), enumerations)
|
||||
val reg = Module(new AsyncResetRegVec(w = width, init = reset))
|
||||
reg.suggestName(name + "_reg")
|
||||
(reg.io, rdesc)
|
||||
}
|
||||
}
|
@ -5,8 +5,11 @@ package freechips.rocketchip.regmapper
|
||||
import Chisel._
|
||||
import chisel3.util.{ReadyValidIO}
|
||||
|
||||
import freechips.rocketchip.util.{SimpleRegIO}
|
||||
import org.json4s.JsonDSL._
|
||||
import org.json4s.JsonAST.JValue
|
||||
import org.json4s.jackson.JsonMethods.{pretty, render}
|
||||
|
||||
import freechips.rocketchip.util.{SimpleRegIO}
|
||||
|
||||
// This information is not used internally by the regmap(...) function.
|
||||
// However, the author of a RegField may be the best person to provide this
|
||||
@ -15,26 +18,47 @@ import freechips.rocketchip.util.{SimpleRegIO}
|
||||
|
||||
object RegFieldAccessType extends scala.Enumeration {
|
||||
type RegFieldAccessType = Value
|
||||
val R, W, RW, RSPECIAL, WSPECIAL, RWSPECIAL, OTHER = Value
|
||||
val R, W, RW = Value
|
||||
}
|
||||
import RegFieldAccessType._
|
||||
|
||||
object RegFieldWrType extends scala.Enumeration {
|
||||
type RegFieldWrType = Value
|
||||
val ONE_TO_CLEAR, ONE_TO_SET, ONE_TO_TOGGLE, ZERO_TO_CLEAR,
|
||||
ZERO_TO_SET, ZERO_TO_TOGGLE, CLEAR, SET, MODIFY = Value
|
||||
}
|
||||
import RegFieldWrType._
|
||||
|
||||
object RegFieldRdAction extends scala.Enumeration {
|
||||
type RegFieldRdAction = Value
|
||||
val CLEAR, SET, MODIFY = Value
|
||||
}
|
||||
import RegFieldRdAction._
|
||||
|
||||
case class RegFieldDesc (
|
||||
name: String,
|
||||
desc: String,
|
||||
group: Option[String] = None,
|
||||
groupDesc: Option[String] = None,
|
||||
access: RegFieldAccessType = RegFieldAccessType.RW,
|
||||
wrType: Option[RegFieldWrType] = None,
|
||||
rdAction: Option[RegFieldRdAction] = None,
|
||||
volatile: Boolean = false,
|
||||
// TODO: testable?
|
||||
reset: Option[BigInt] = None,
|
||||
enumerations: Map[BigInt, (String, String)] = Map()
|
||||
){
|
||||
}
|
||||
|
||||
// Our descriptions are in terms of RegFields only, which is somewhat unusual for
|
||||
// developers who are used to things being defined as bitfields within registers.
|
||||
// The "Group" allows a string & (optional) description to be added which describes the conceptual "Group"
|
||||
// the RegField belongs to. This can be used by downstream flows as they see fit to
|
||||
// present the information.
|
||||
object RegFieldDesc {
|
||||
def reserved: RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0))
|
||||
}
|
||||
|
||||
// Our descriptions are in terms of RegFields only, which is somewhat
|
||||
// unusual for developers who are used to things being defined as bitfields
|
||||
// within registers. The "Group" allows a string & (optional) description
|
||||
// to be added which describes the conceptual "Group" the RegField belongs to.
|
||||
// This can be used by downstream flows as they see fit to present the information.
|
||||
|
||||
object RegFieldGroup {
|
||||
def apply (name: String, desc: Option[String], regs: Seq[RegField], descFirstOnly: Boolean = true): Seq[RegField] = {
|
||||
@ -116,8 +140,29 @@ object RegWriteFn
|
||||
case class RegField(width: Int, read: RegReadFn, write: RegWriteFn, desc: Option[RegFieldDesc])
|
||||
{
|
||||
require (width > 0, s"RegField width must be > 0, not $width")
|
||||
|
||||
def pipelined = !read.combinational || !write.combinational
|
||||
|
||||
def readOnly = this.copy(write = (), desc = this.desc.map(_.copy(access = RegFieldAccessType.R)))
|
||||
|
||||
def toJson(byteOffset: Int, bitOffset: Int): JValue = {
|
||||
( ("byteOffset" -> s"0x${byteOffset.toHexString}") ~
|
||||
("bitOffset" -> bitOffset) ~
|
||||
("bitWidth" -> width) ~
|
||||
("name" -> desc.map(_.name)) ~
|
||||
("description" -> desc.map{ d=> if (d.desc == "") None else Some(d.desc)}) ~
|
||||
("resetValue" -> desc.map{_.reset}) ~
|
||||
("group" -> desc.map{_.group}) ~
|
||||
("groupDesc" -> desc.map{_.groupDesc}) ~
|
||||
("accessType" -> desc.map {d => d.access.toString}) ~
|
||||
("writeType" -> desc.map {d => d.wrType.map(_.toString)}) ~
|
||||
("readAction" -> desc.map {d => d.rdAction.map(_.toString)}) ~
|
||||
("volatile" -> desc.map {d => if (d.volatile) Some(true) else None}) ~
|
||||
("enumerations" -> desc.map {d =>
|
||||
Option(d.enumerations.map { case (key, (name, edesc)) =>
|
||||
(("value" -> key) ~ ("name" -> name) ~ ("description" -> edesc))
|
||||
}).filter(_.nonEmpty)}) )
|
||||
}
|
||||
}
|
||||
|
||||
object RegField
|
||||
@ -125,8 +170,7 @@ object RegField
|
||||
// Byte address => sequence of bitfields, lowest index => lowest address
|
||||
type Map = (Int, Seq[RegField])
|
||||
|
||||
def apply(n: Int) : RegField = apply(n, (), (),
|
||||
Some(RegFieldDesc("reserved", "", access = RegFieldAccessType.R, reset = Some(0))))
|
||||
def apply(n: Int) : RegField = apply(n, (), (), Some(RegFieldDesc.reserved))
|
||||
|
||||
def apply(n: Int, r: RegReadFn, w: RegWriteFn) : RegField = apply(n, r, w, None)
|
||||
def apply(n: Int, r: RegReadFn, w: RegWriteFn, desc: RegFieldDesc) : RegField = apply(n, r, w, Some(desc))
|
||||
@ -142,7 +186,7 @@ object RegField
|
||||
// Setting takes priority over clearing.
|
||||
def w1ToClear(n: Int, reg: UInt, set: UInt, desc: Option[RegFieldDesc] = None): RegField =
|
||||
RegField(n, reg, RegWriteFn((valid, data) => { reg := ~(~reg | Mux(valid, data, UInt(0))) | set; Bool(true) }),
|
||||
desc.map{_.copy(access = RegFieldAccessType.RWSPECIAL)})
|
||||
desc.map{_.copy(access = RegFieldAccessType.RW, wrType=Some(RegFieldWrType.ONE_TO_CLEAR), volatile = true)})
|
||||
|
||||
// This RegField wraps an explicit register
|
||||
// (e.g. Black-Boxed Register) to create a R/W register.
|
||||
@ -151,26 +195,41 @@ object RegField
|
||||
bb.en := valid
|
||||
bb.d := data
|
||||
Bool(true)
|
||||
}), desc.map{_.copy(access = RegFieldAccessType.RW)})
|
||||
}), desc)
|
||||
|
||||
// Create byte-sized read-write RegFields out of a large UInt register.
|
||||
// It is updated when any of the bytes are written. Because the RegFields
|
||||
// are all byte-sized, this is also suitable when a register is larger
|
||||
// It is updated when any of the (implemented) bytes are written, the non-written
|
||||
// bytes are just copied over from their current value.
|
||||
// Because the RegField are all byte-sized, this is also suitable when a register is larger
|
||||
// than the intended bus width of the device (atomic updates are impossible).
|
||||
def bytes(reg: UInt, numBytes: Int, desc: Option[RegFieldDesc]): Seq[RegField] = {
|
||||
require(reg.getWidth * 8 >= numBytes, "Can't break a ${reg.getWidth}-bit-wide register into only ${numBytes} bytes.")
|
||||
val numFullBytes = reg.getWidth/8
|
||||
val numPartialBytes = if ((reg.getWidth % 8) > 0) 1 else 0
|
||||
val numPadBytes = numBytes - numFullBytes - numPartialBytes
|
||||
val pad = reg | UInt(0, width = 8*numBytes)
|
||||
val oldBytes = Vec.tabulate(numBytes) { i => pad(8*(i+1)-1, 8*i) }
|
||||
val newBytes = Wire(init = oldBytes)
|
||||
val valids = Wire(init = Vec.fill(numBytes) { Bool(false) })
|
||||
when (valids.reduce(_ || _)) { reg := newBytes.asUInt }
|
||||
Seq.tabulate(numBytes) { i =>
|
||||
val newDesc = desc.map {d => d.copy(name = d.name + s"_$i")}
|
||||
RegField(8, oldBytes(i),
|
||||
RegWriteFn((valid, data) => {
|
||||
|
||||
def wrFn(i: Int): RegWriteFn = RegWriteFn((valid, data) => {
|
||||
valids(i) := valid
|
||||
when (valid) { newBytes(i) := data }
|
||||
when (valid) {newBytes(i) := data}
|
||||
Bool(true)
|
||||
}), newDesc)}}
|
||||
})
|
||||
|
||||
val fullBytes = Seq.tabulate(numFullBytes) { i =>
|
||||
val newDesc = desc.map {d => d.copy(name = d.name + s"_$i")}
|
||||
RegField(8, oldBytes(i), wrFn(i), newDesc)}
|
||||
val partialBytes = if (numPartialBytes > 0) {
|
||||
val newDesc = desc.map {d => d.copy(name = d.name + s"_$numFullBytes")}
|
||||
Seq(RegField(reg.getWidth % 8, oldBytes(numFullBytes), wrFn(numFullBytes), newDesc),
|
||||
RegField(8 - (reg.getWidth % 8)))
|
||||
} else Nil
|
||||
val padBytes = Seq.fill(numPadBytes){RegField(8)}
|
||||
fullBytes ++ partialBytes ++ padBytes
|
||||
}
|
||||
|
||||
def bytes(reg: UInt, desc: Option[RegFieldDesc]): Seq[RegField] = {
|
||||
val width = reg.getWidth
|
||||
|
@ -15,16 +15,20 @@ import freechips.rocketchip.interrupts._
|
||||
import freechips.rocketchip.util.property._
|
||||
|
||||
trait BusErrors extends Bundle {
|
||||
def toErrorList: List[Option[Valid[UInt]]]
|
||||
def toErrorList: List[Option[(Valid[UInt], String, String)]]
|
||||
}
|
||||
|
||||
class L1BusErrors(implicit p: Parameters) extends CoreBundle()(p) with BusErrors {
|
||||
val icache = new ICacheErrors
|
||||
val dcache = new DCacheErrors
|
||||
|
||||
def toErrorList =
|
||||
List(None, None, icache.correctable, icache.uncorrectable,
|
||||
None, Some(dcache.bus), dcache.correctable, dcache.uncorrectable)
|
||||
def toErrorList = List(None, None,
|
||||
icache.correctable.map((_, "I_CORRECTABLE", "Instruction cache or ITIM correctable ECC error ")),
|
||||
icache.uncorrectable.map((_, "I_UNCORRECTABLE", "ITIM uncorrectable ECC error")),
|
||||
None,
|
||||
Some((dcache.bus, "DBUS", "Load or store TileLink bus error")),
|
||||
dcache.correctable.map((_, "D_CORRECTABLE", "Data cache correctable ECC error")),
|
||||
dcache.uncorrectable.map((_, "D_UNCORRECTABLE", "Data cache uncorrectable ECC error")))
|
||||
}
|
||||
|
||||
case class BusErrorUnitParams(addr: BigInt, size: Int = 4096)
|
||||
@ -44,14 +48,41 @@ class BusErrorUnit[T <: BusErrors](t: => T, params: BusErrorUnitParams)(implicit
|
||||
val interrupt = Bool().asOutput
|
||||
})
|
||||
|
||||
val sources = io.errors.toErrorList
|
||||
val cause = Reg(init = UInt(0, log2Ceil(sources.lastIndexWhere(_.nonEmpty) + 1)))
|
||||
val value = Reg(UInt(width = sources.flatten.map(_.bits.getWidth).max))
|
||||
val sources_and_desc = io.errors.toErrorList
|
||||
val sources = sources_and_desc.map(_.map(_._1))
|
||||
val sources_enums = sources_and_desc.zipWithIndex.flatMap{case (s, i) => s.map {e => (BigInt(i) -> (e._2, e._3))}}
|
||||
|
||||
val causeWidth = log2Ceil(sources.lastIndexWhere(_.nonEmpty) + 1)
|
||||
val (cause, cause_desc) = DescribedReg(UInt(causeWidth.W),
|
||||
"cause", "Cause of error event", reset=Some(0.U(causeWidth.W)), volatile=true, enumerations=sources_enums.toMap)
|
||||
|
||||
val (value, value_desc) = DescribedReg(UInt(width = sources.flatten.map(_.bits.getWidth).max),
|
||||
"value", "Physical address of error event", reset=None, volatile=true)
|
||||
require(value.getWidth <= regWidth)
|
||||
|
||||
val enable = Reg(init = Vec(sources.map(_.nonEmpty.B)))
|
||||
val enable_desc = sources.zipWithIndex.map { case (s, i) =>
|
||||
if (s.nonEmpty) RegFieldDesc(s"enable_$i", "", reset=Some(1))
|
||||
else RegFieldDesc.reserved
|
||||
}
|
||||
|
||||
val global_interrupt = Reg(init = Vec.fill(sources.size)(false.B))
|
||||
val global_interrupt_desc = sources.zipWithIndex.map { case (s, i) =>
|
||||
if (s.nonEmpty) RegFieldDesc(s"plic_interrupt_$i", "", reset=Some(0))
|
||||
else RegFieldDesc.reserved
|
||||
}
|
||||
|
||||
val accrued = Reg(init = Vec.fill(sources.size)(false.B))
|
||||
val accrued_desc = sources.zipWithIndex.map { case (s, i) =>
|
||||
if (s.nonEmpty) RegFieldDesc(s"accrued_$i", "", reset=Some(0), volatile = true)
|
||||
else RegFieldDesc.reserved
|
||||
}
|
||||
|
||||
val local_interrupt = Reg(init = Vec.fill(sources.size)(false.B))
|
||||
val local_interrupt_desc = sources.zipWithIndex.map { case (s, i) =>
|
||||
if (s.nonEmpty) RegFieldDesc(s"local_interrupt_$i", "", reset=Some(0))
|
||||
else RegFieldDesc.reserved
|
||||
}
|
||||
|
||||
for ((((s, en), acc), i) <- (sources zip enable zip accrued).zipWithIndex; if s.nonEmpty) {
|
||||
when (s.get.valid) {
|
||||
@ -68,17 +99,18 @@ class BusErrorUnit[T <: BusErrors](t: => T, params: BusErrorUnitParams)(implicit
|
||||
io.interrupt := (accrued.asUInt & local_interrupt.asUInt).orR
|
||||
int_out(0) := (accrued.asUInt & global_interrupt.asUInt).orR
|
||||
|
||||
def reg(r: UInt) = RegField.bytes(r, (r.getWidth + 7)/8)
|
||||
def reg(v: Vec[Bool]) = v.map(r => RegField(1, r))
|
||||
def numberRegs(x: Seq[Seq[RegField]]) = x.zipWithIndex.map { case (f, i) => (i * regWidth / 8) -> f }
|
||||
def reg(r: UInt, gn: String, d: RegFieldDesc) = RegFieldGroup(gn, None, RegField.bytes(r, (r.getWidth + 7)/8, Some(d)))
|
||||
def reg(v: Vec[Bool], gn: String, gd: String, d: Seq[RegFieldDesc]) =
|
||||
RegFieldGroup(gn, Some(gd), (v zip d).map {case (r, rd) => RegField(1, r, rd)})
|
||||
def numberRegs(x: Seq[Seq[RegField]]) = x.zipWithIndex.map {case (f, i) => (i * regWidth / 8) -> f }
|
||||
|
||||
node.regmap(numberRegs(Seq(
|
||||
reg(cause),
|
||||
reg(value),
|
||||
reg(enable),
|
||||
reg(global_interrupt),
|
||||
reg(accrued),
|
||||
reg(local_interrupt))):_*)
|
||||
reg(cause, "cause", cause_desc),
|
||||
reg(value, "value", value_desc),
|
||||
reg(enable, "enable", "Event enable mask", enable_desc),
|
||||
reg(global_interrupt, "plic_interrupt", "Platform-level interrupt enable mask", global_interrupt_desc),
|
||||
reg(accrued, "accrued", "Accrued event mask" ,accrued_desc),
|
||||
reg(local_interrupt, "local_interrupt", "Hart-local interrupt-enable mask", local_interrupt_desc))):_*)
|
||||
|
||||
// hardwire mask bits for unsupported sources to 0
|
||||
for ((s, i) <- sources.zipWithIndex; if s.isEmpty) {
|
||||
|
@ -357,14 +357,14 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param
|
||||
CSRs.mip -> read_mip,
|
||||
CSRs.mie -> reg_mie,
|
||||
CSRs.mscratch -> reg_mscratch,
|
||||
CSRs.mepc -> reg_mepc.sextTo(xLen),
|
||||
CSRs.mepc -> readEPC(reg_mepc).sextTo(xLen),
|
||||
CSRs.mbadaddr -> reg_mbadaddr.sextTo(xLen),
|
||||
CSRs.mcause -> reg_mcause,
|
||||
CSRs.mhartid -> io.hartid)
|
||||
|
||||
val debug_csrs = LinkedHashMap[Int,Bits](
|
||||
CSRs.dcsr -> reg_dcsr.asUInt,
|
||||
CSRs.dpc -> reg_dpc.sextTo(xLen),
|
||||
CSRs.dpc -> readEPC(reg_dpc).sextTo(xLen),
|
||||
CSRs.dscratch -> reg_dscratch.asUInt)
|
||||
|
||||
val fp_csrs = LinkedHashMap[Int,Bits](
|
||||
@ -431,7 +431,7 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param
|
||||
read_mapping += CSRs.scause -> reg_scause
|
||||
read_mapping += CSRs.sbadaddr -> reg_sbadaddr.sextTo(xLen)
|
||||
read_mapping += CSRs.sptbr -> reg_sptbr.asUInt
|
||||
read_mapping += CSRs.sepc -> reg_sepc.sextTo(xLen)
|
||||
read_mapping += CSRs.sepc -> readEPC(reg_sepc).sextTo(xLen)
|
||||
read_mapping += CSRs.stvec -> reg_stvec.sextTo(xLen)
|
||||
read_mapping += CSRs.scounteren -> reg_scounteren
|
||||
read_mapping += CSRs.mideleg -> reg_mideleg
|
||||
@ -584,17 +584,17 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param
|
||||
reg_mstatus.spie := true
|
||||
reg_mstatus.spp := PRV.U
|
||||
new_prv := reg_mstatus.spp
|
||||
io.evec := reg_sepc
|
||||
io.evec := readEPC(reg_sepc)
|
||||
}.elsewhen (Bool(usingDebug) && io.rw.addr(10)) {
|
||||
new_prv := reg_dcsr.prv
|
||||
reg_debug := false
|
||||
io.evec := reg_dpc
|
||||
io.evec := readEPC(reg_dpc)
|
||||
}.otherwise {
|
||||
reg_mstatus.mie := reg_mstatus.mpie
|
||||
reg_mstatus.mpie := true
|
||||
reg_mstatus.mpp := legalizePrivilege(PRV.U)
|
||||
new_prv := reg_mstatus.mpp
|
||||
io.evec := reg_mepc
|
||||
io.evec := readEPC(reg_mepc)
|
||||
}
|
||||
}
|
||||
|
||||
@ -640,9 +640,12 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param
|
||||
when (decoded_addr(CSRs.misa)) {
|
||||
val mask = UInt(isaStringToMask(isaMaskString), xLen)
|
||||
val f = wdata('f' - 'a')
|
||||
// suppress write if it would cause the next fetch to be misaligned
|
||||
when (!usingCompressed || !io.pc(1) || wdata('c' - 'a')) {
|
||||
if (coreParams.misaWritable)
|
||||
reg_misa := ~(~wdata | (!f << ('d' - 'a'))) & mask | reg_misa & ~mask
|
||||
}
|
||||
}
|
||||
when (decoded_addr(CSRs.mip)) {
|
||||
// MIP should be modified based on the value in reg_mip, not the value
|
||||
// in read_mip, since read_mip.seip is the OR of reg_mip.seip and
|
||||
@ -838,5 +841,6 @@ class CSRFile(perfEventSets: EventSets = new EventSets(Seq()))(implicit p: Param
|
||||
}
|
||||
}
|
||||
def formEPC(x: UInt) = ~(~x | (if (usingCompressed) 1.U else 3.U))
|
||||
def readEPC(x: UInt) = ~(~x | Mux(reg_misa('c' - 'a'), 1.U, 3.U))
|
||||
def isaStringToMask(s: String) = s.map(x => 1 << (x - 'A')).foldLeft(0)(_|_)
|
||||
}
|
||||
|
@ -15,16 +15,15 @@ import TLMessages._
|
||||
|
||||
class DCacheErrors(implicit p: Parameters) extends L1HellaCacheBundle()(p)
|
||||
with CanHaveErrors {
|
||||
val correctable = (cacheParams.tagECC.canCorrect || cacheParams.dataECC.canCorrect).option(Valid(UInt(width = paddrBits)))
|
||||
val uncorrectable = (cacheParams.tagECC.canDetect || cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||
val correctable = (cacheParams.tagCode.canCorrect || cacheParams.dataCode.canCorrect).option(Valid(UInt(width = paddrBits)))
|
||||
val uncorrectable = (cacheParams.tagCode.canDetect || cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||
val bus = Valid(UInt(width = paddrBits))
|
||||
}
|
||||
|
||||
class DCacheDataReq(implicit p: Parameters) extends L1HellaCacheBundle()(p) {
|
||||
val eccBytes = cacheParams.dataECCBytes
|
||||
val addr = Bits(width = untagBits)
|
||||
val write = Bool()
|
||||
val wdata = UInt(width = cacheParams.dataECC.width(eccBytes*8) * rowBytes/eccBytes)
|
||||
val wdata = UInt(width = encBits * rowBytes / eccBytes)
|
||||
val wordMask = UInt(width = rowBytes / wordBytes)
|
||||
val eccMask = UInt(width = wordBytes / eccBytes)
|
||||
val way_en = Bits(width = nWays)
|
||||
@ -37,9 +36,6 @@ class DCacheDataArray(implicit p: Parameters) extends L1HellaCacheModule()(p) {
|
||||
}
|
||||
|
||||
require(rowBytes % wordBytes == 0)
|
||||
val eccBits = cacheParams.dataECCBytes * 8
|
||||
val encBits = cacheParams.dataECC.width(eccBits)
|
||||
val encWordBits = encBits * (wordBits / eccBits)
|
||||
val eccMask = if (eccBits == wordBits) Seq(true.B) else io.req.bits.eccMask.toBools
|
||||
val wMask = if (nWays == 1) eccMask else (0 until nWays).flatMap(i => eccMask.map(_ && io.req.bits.way_en(i)))
|
||||
val wWords = io.req.bits.wdata.grouped(encBits * (wordBits / eccBits))
|
||||
@ -69,11 +65,8 @@ class DCache(hartid: Int, val scratch: () => Option[AddressSet] = () => None, va
|
||||
}
|
||||
|
||||
class DCacheModule(outer: DCache) extends HellaCacheModule(outer) {
|
||||
// no tag ECC support
|
||||
val tECC = cacheParams.tagECC
|
||||
val dECC = cacheParams.dataECC
|
||||
val eccBytes = cacheParams.dataECCBytes
|
||||
val eccBits = eccBytes * 8
|
||||
val tECC = cacheParams.tagCode
|
||||
val dECC = cacheParams.dataCode
|
||||
require(isPow2(eccBytes) && eccBytes <= wordBytes)
|
||||
require(eccBytes == 1 || !dECC.isInstanceOf[IdentityCode])
|
||||
val usingRMW = eccBytes > 1 || usingAtomicsInCache
|
||||
@ -700,7 +693,9 @@ class DCacheModule(outer: DCache) extends HellaCacheModule(outer) {
|
||||
when (s2_correct) { pstore1_storegen_data := s2_data_word_corrected }
|
||||
|
||||
// flushes
|
||||
val resetting = Reg(init=Bool(!usingDataScratchpad))
|
||||
val resetting = RegInit(false.B)
|
||||
if (!usingDataScratchpad)
|
||||
when (RegNext(reset)) { resetting := true }
|
||||
val flushed = Reg(init=Bool(true))
|
||||
val flushing = Reg(init=Bool(false))
|
||||
val flushCounter = Reg(init=UInt(nSets * (nWays-1), log2Ceil(nSets * nWays)))
|
||||
|
@ -19,8 +19,8 @@ case class DCacheParams(
|
||||
nWays: Int = 4,
|
||||
rowBits: Int = 64,
|
||||
nTLBEntries: Int = 32,
|
||||
tagECC: Code = new IdentityCode,
|
||||
dataECC: Code = new IdentityCode,
|
||||
tagECC: Option[String] = None,
|
||||
dataECC: Option[String] = None,
|
||||
dataECCBytes: Int = 1,
|
||||
nMSHRs: Int = 1,
|
||||
nSDQ: Int = 17,
|
||||
@ -31,6 +31,9 @@ case class DCacheParams(
|
||||
pipelineWayMux: Boolean = false,
|
||||
scratch: Option[BigInt] = None) extends L1CacheParams {
|
||||
|
||||
def tagCode: Code = Code.fromString(tagECC)
|
||||
def dataCode: Code = Code.fromString(dataECC)
|
||||
|
||||
def dataScratchpadBytes: Int = scratch.map(_ => nSets*blockBytes).getOrElse(0)
|
||||
|
||||
def replacement = new RandomReplacement(nWays)
|
||||
@ -58,7 +61,11 @@ trait HasL1HellaCacheParameters extends HasL1CacheParameters with HasCoreParamet
|
||||
def offsetlsb = wordOffBits
|
||||
def rowWords = rowBits/wordBits
|
||||
def doNarrowRead = coreDataBits * nWays % rowBits == 0
|
||||
def encDataBits = cacheParams.dataECC.width(coreDataBits)
|
||||
def eccBytes = cacheParams.dataECCBytes
|
||||
val eccBits = cacheParams.dataECCBytes * 8
|
||||
val encBits = cacheParams.dataCode.width(eccBits)
|
||||
val encWordBits = encBits * (wordBits / eccBits)
|
||||
def encDataBits = cacheParams.dataCode.width(coreDataBits) // NBDCache only
|
||||
def encRowBits = encDataBits*rowWords
|
||||
def lrscCycles = 32 // ISA requires 16-insn LRSC sequences to succeed
|
||||
def lrscBackoff = 3 // disallow LRSC reacquisition briefly
|
||||
|
@ -20,13 +20,15 @@ case class ICacheParams(
|
||||
rowBits: Int = 128,
|
||||
nTLBEntries: Int = 32,
|
||||
cacheIdBits: Int = 0,
|
||||
tagECC: Code = new IdentityCode,
|
||||
dataECC: Code = new IdentityCode,
|
||||
tagECC: Option[String] = None,
|
||||
dataECC: Option[String] = None,
|
||||
itimAddr: Option[BigInt] = None,
|
||||
prefetch: Boolean = false,
|
||||
blockBytes: Int = 64,
|
||||
latency: Int = 2,
|
||||
fetchBytes: Int = 4) extends L1CacheParams {
|
||||
def tagCode: Code = Code.fromString(tagECC)
|
||||
def dataCode: Code = Code.fromString(dataECC)
|
||||
def replacement = new RandomReplacement(nWays)
|
||||
}
|
||||
|
||||
@ -41,8 +43,8 @@ class ICacheReq(implicit p: Parameters) extends CoreBundle()(p) with HasL1ICache
|
||||
class ICacheErrors(implicit p: Parameters) extends CoreBundle()(p)
|
||||
with HasL1ICacheParameters
|
||||
with CanHaveErrors {
|
||||
val correctable = (cacheParams.tagECC.canDetect || cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||
val uncorrectable = (cacheParams.itimAddr.nonEmpty && cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||
val correctable = (cacheParams.tagCode.canDetect || cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||
val uncorrectable = (cacheParams.itimAddr.nonEmpty && cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||
}
|
||||
|
||||
class ICache(val icacheParams: ICacheParams, val hartId: Int)(implicit p: Parameters) extends LazyModule {
|
||||
@ -113,8 +115,8 @@ class ICacheModule(outer: ICache) extends LazyModuleImp(outer)
|
||||
// Option.unzip does not exist :-(
|
||||
val (tl_in, edge_in) = outer.slaveNode.in.headOption.unzip
|
||||
|
||||
val tECC = cacheParams.tagECC
|
||||
val dECC = cacheParams.dataECC
|
||||
val tECC = cacheParams.tagCode
|
||||
val dECC = cacheParams.dataCode
|
||||
|
||||
require(isPow2(nSets) && isPow2(nWays))
|
||||
require(!usingVM || pgIdxBits >= untagBits)
|
||||
|
@ -668,8 +668,8 @@ class NonBlockingDCacheModule(outer: NonBlockingDCache) extends HellaCacheModule
|
||||
require(dataScratchpadSize == 0)
|
||||
|
||||
// ECC is only supported on the data array
|
||||
require(cacheParams.tagECC.isInstanceOf[IdentityCode])
|
||||
val dECC = cacheParams.dataECC
|
||||
require(cacheParams.tagCode.isInstanceOf[IdentityCode])
|
||||
val dECC = cacheParams.dataCode
|
||||
|
||||
val wb = Module(new WritebackUnit)
|
||||
val prober = Module(new ProbeUnit)
|
||||
|
@ -228,14 +228,12 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
|
||||
bpu.io.pc := ibuf.io.pc
|
||||
bpu.io.ea := mem_reg_wdata
|
||||
|
||||
val id_pc_misaligned = !csr.io.status.isa('c'-'a') && ibuf.io.pc(1)
|
||||
val id_xcpt0 = ibuf.io.inst(0).bits.xcpt0
|
||||
val id_xcpt1 = ibuf.io.inst(0).bits.xcpt1
|
||||
val (id_xcpt, id_cause) = checkExceptions(List(
|
||||
(csr.io.interrupt, csr.io.interrupt_cause),
|
||||
(bpu.io.debug_if, UInt(CSR.debugTriggerCause)),
|
||||
(bpu.io.xcpt_if, UInt(Causes.breakpoint)),
|
||||
(id_pc_misaligned, UInt(Causes.misaligned_fetch)),
|
||||
(id_xcpt0.pf.inst, UInt(Causes.fetch_page_fault)),
|
||||
(id_xcpt0.ae.inst, UInt(Causes.fetch_access)),
|
||||
(id_xcpt1.pf.inst, UInt(Causes.fetch_page_fault)),
|
||||
@ -572,7 +570,7 @@ class Rocket(implicit p: Parameters) extends CoreModule()(p)
|
||||
sboard.clear(ll_wen, ll_waddr)
|
||||
def id_sboard_clear_bypass(r: UInt) = {
|
||||
// ll_waddr arrives late when D$ has ECC, so reshuffle the hazard check
|
||||
if (tileParams.dcache.get.dataECC.isInstanceOf[IdentityCode]) ll_wen && ll_waddr === r
|
||||
if (!tileParams.dcache.get.dataECC.isDefined) ll_wen && ll_waddr === r
|
||||
else div.io.resp.fire() && div.io.resp.bits.tag === r || dmem_resp_replay && dmem_resp_xpu && dmem_resp_waddr === r
|
||||
}
|
||||
val id_sboard_hazard = checkHazards(hazard_targets, rd => sboard.read(rd) && !id_sboard_clear_bypass(rd))
|
||||
|
@ -97,39 +97,22 @@ abstract class BaseSubsystem(implicit p: Parameters) extends BareSubsystem {
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseSubsystemModuleImp[+L <: BaseSubsystem](_outer: L) extends BareSubsystemModuleImp(_outer) {
|
||||
println("Generated Address Map")
|
||||
private val aw = (outer.sbus.busView.bundle.addressBits-1)/4 + 1
|
||||
private val fmt = s"\t%${aw}x - %${aw}x %c%c%c%c%c %s"
|
||||
|
||||
private def collect(path: List[String], value: ResourceValue): List[(String, ResourceAddress)] = {
|
||||
value match {
|
||||
case r: ResourceAddress => List((path(1), r))
|
||||
case b: ResourceMapping => List((path(1), ResourceAddress(b.address, b.permissions)))
|
||||
case ResourceMap(value, _) => value.toList.flatMap { case (key, seq) => seq.flatMap(r => collect(key :: path, r)) }
|
||||
case _ => Nil
|
||||
}
|
||||
}
|
||||
private val ranges = collect(Nil, outer.bindingTree).groupBy(_._2).toList.flatMap { case (key, seq) =>
|
||||
AddressRange.fromSets(key.address).map { r => (r, key.permissions, seq.map(_._1)) }
|
||||
}.sortBy(_._1)
|
||||
private val json = ranges.map { case (range, ResourcePermissions(r, w, x, c, a), names) =>
|
||||
println(fmt.format(
|
||||
range.base,
|
||||
range.base+range.size,
|
||||
if (a) 'A' else ' ',
|
||||
if (r) 'R' else ' ',
|
||||
if (w) 'W' else ' ',
|
||||
if (x) 'X' else ' ',
|
||||
if (c) 'C' else ' ',
|
||||
names.mkString(", ")))
|
||||
s"""{"base":[${range.base}],"size":[${range.size}],"r":[$r],"w":[$w],"x":[$x],"c":[$c],"a":[$a],"names":[${names.map('"'+_+'"').mkString(",")}]}"""
|
||||
abstract class BaseSubsystemModuleImp[+L <: BaseSubsystem](_outer: L) extends BareSubsystemModuleImp(_outer) {
|
||||
private val mapping: Seq[AddressMapEntry] = {
|
||||
outer.collectResourceAddresses.groupBy(_._2).toList.flatMap { case (key, seq) =>
|
||||
AddressRange.fromSets(key.address).map { r => AddressMapEntry(r, key.permissions, seq.map(_._1)) }
|
||||
}.sortBy(_.range)
|
||||
}
|
||||
|
||||
println("Generated Address Map")
|
||||
mapping.map(entry => println(entry.toString((outer.sbus.busView.bundle.addressBits-1)/4 + 1)))
|
||||
println("")
|
||||
ElaborationArtefacts.add("memmap.json", s"""{"mapping":[${json.mkString(",")}]}""")
|
||||
|
||||
ElaborationArtefacts.add("memmap.json", s"""{"mapping":[${mapping.map(_.serialize).mkString(",")}]}""")
|
||||
|
||||
// Confirm that all of memory was described by DTS
|
||||
private val dtsRanges = AddressRange.unify(ranges.map(_._1))
|
||||
private val dtsRanges = AddressRange.unify(mapping.map(_.range))
|
||||
private val allRanges = AddressRange.unify(outer.topManagers.get.flatMap { m => AddressRange.fromSets(m.address) })
|
||||
|
||||
if (dtsRanges != allRanges) {
|
||||
|
@ -12,7 +12,7 @@ case class FrontBusParams(
|
||||
beatBytes: Int,
|
||||
blockBytes: Int,
|
||||
sbusCrossing: SubsystemClockCrossing = SynchronousCrossing(),
|
||||
sbusBuffer: BufferParams = BufferParams.default) extends HasTLBusParams
|
||||
sbusBuffer: BufferParams = BufferParams.none) extends HasTLBusParams
|
||||
|
||||
case object FrontBusKey extends Field[FrontBusParams]
|
||||
|
||||
@ -23,21 +23,23 @@ class FrontBus(params: FrontBusParams)
|
||||
val crossing = params.sbusCrossing
|
||||
|
||||
def fromPort[D,U,E,B <: Data]
|
||||
(name: Option[String] = None, buffers: Int = 1)
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
||||
from("port" named name) { fixFrom(TLFIFOFixer.all, buffers) :=* gen }
|
||||
from("port" named name) { fixFrom(TLFIFOFixer.all, buffer) :=* gen }
|
||||
}
|
||||
|
||||
def fromMasterNode(name: Option[String] = None, buffers: Int = 1)(gen: TLOutwardNode) {
|
||||
from("master" named name) { fixFrom(TLFIFOFixer.all, buffers) :=* gen }
|
||||
def fromMasterNode
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: TLOutwardNode) {
|
||||
from("master" named name) { fixFrom(TLFIFOFixer.all, buffer) :=* gen }
|
||||
}
|
||||
|
||||
def fromMaster[D,U,E,B <: Data]
|
||||
(name: Option[String] = None, buffers: Int = 1)
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
||||
from("master" named name) { fixFrom(TLFIFOFixer.all, buffers) :=* gen }
|
||||
from("master" named name) { fixFrom(TLFIFOFixer.all, buffer) :=* gen }
|
||||
}
|
||||
|
||||
def fromCoherentChip(gen: => TLNode): TLInwardNode = {
|
||||
|
@ -44,9 +44,8 @@ case object MemoryBusKey extends Field[MemoryBusParams]
|
||||
class MemoryBus(params: MemoryBusParams)(implicit p: Parameters) extends TLBusWrapper(params, "memory_bus")(p)
|
||||
with HasTLXbarPhy {
|
||||
|
||||
def fromCoherenceManager(
|
||||
name: Option[String] = None,
|
||||
buffer: BufferParams = BufferParams.none)
|
||||
def fromCoherenceManager
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => TLNode): TLInwardNode = {
|
||||
from("coherence_manager" named name) {
|
||||
inwardNode := TLBuffer(buffer) := gen
|
||||
|
@ -108,10 +108,10 @@ class PeripheryBus(params: PeripheryBusParams)
|
||||
|
||||
|
||||
def toTile
|
||||
(name: Option[String] = None, buffers: Int = 0)
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => TLNode): TLOutwardNode = {
|
||||
to("tile" named name) { FlipRendering { implicit p =>
|
||||
gen :*= bufferTo(buffers)
|
||||
gen :*= bufferTo(buffer)
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ trait HasSlaveAXI4Port { this: BaseSubsystem =>
|
||||
id = IdRange(0, 1 << params.idBits))))))
|
||||
|
||||
private val fifoBits = 1
|
||||
sbus.fromPort(Some(portName)) {
|
||||
fbus.fromPort(Some(portName), buffer = BufferParams.default) {
|
||||
(TLWidthWidget(params.beatBytes)
|
||||
:= AXI4ToTL()
|
||||
:= AXI4UserYanker(Some(1 << (params.sourceBits - fifoBits - 1)))
|
||||
|
@ -21,8 +21,8 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters) extends TLBusWr
|
||||
private val master_splitter = LazyModule(new TLSplitter)
|
||||
inwardNode :=* master_splitter.node
|
||||
|
||||
protected def fixFromThenSplit(policy: TLFIFOFixer.Policy, buffers: Int): TLInwardNode =
|
||||
master_splitter.node :=* TLBuffer.chain(buffers).foldLeft(TLFIFOFixer(policy))(_ :=* _)
|
||||
protected def fixFromThenSplit(policy: TLFIFOFixer.Policy, buffer: BufferParams): TLInwardNode =
|
||||
master_splitter.node :=* TLBuffer(buffer) :=* TLFIFOFixer(policy)
|
||||
|
||||
def busView = master_splitter.node.edges.in.head
|
||||
|
||||
@ -72,10 +72,10 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters) extends TLBusWr
|
||||
}
|
||||
|
||||
def fromTile
|
||||
(name: Option[String], buffers: Int = 0, cork: Option[Boolean] = None)
|
||||
(name: Option[String], buffer: BufferParams = BufferParams.none, cork: Option[Boolean] = None)
|
||||
(gen: => TLNode): TLInwardNode = {
|
||||
from("tile" named name) {
|
||||
fixFromThenSplit(TLFIFOFixer.allUncacheable, buffers) :=* gen
|
||||
fixFromThenSplit(TLFIFOFixer.allUncacheable, buffer) :=* gen
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,23 +87,23 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters) extends TLBusWr
|
||||
}
|
||||
|
||||
def fromPort[D,U,E,B <: Data]
|
||||
(name: Option[String] = None, buffers: Int = 0)
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
||||
from("port" named name) { fixFromThenSplit(TLFIFOFixer.all, buffers) :=* gen }
|
||||
from("port" named name) { fixFromThenSplit(TLFIFOFixer.all, buffer) :=* gen }
|
||||
}
|
||||
|
||||
def fromCoherentMaster[D,U,E,B <: Data]
|
||||
(name: Option[String] = None, buffers: Int = 0)
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
||||
from("coherent_master" named name) { fixFrom(TLFIFOFixer.all, buffers) :=* gen }
|
||||
from("coherent_master" named name) { fixFrom(TLFIFOFixer.all, buffer) :=* gen }
|
||||
}
|
||||
|
||||
def fromMaster[D,U,E,B <: Data]
|
||||
(name: Option[String] = None, buffers: Int = 0)
|
||||
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
||||
from("master" named name) { fixFromThenSplit(TLFIFOFixer.all, buffers) :=* gen }
|
||||
from("master" named name) { fixFromThenSplit(TLFIFOFixer.all, buffer) :=* gen }
|
||||
}
|
||||
}
|
||||
|
@ -52,8 +52,6 @@ class RoCCCoreIO(implicit p: Parameters) extends CoreBundle()(p) {
|
||||
val busy = Bool(OUTPUT)
|
||||
val interrupt = Bool(OUTPUT)
|
||||
val exception = Bool(INPUT)
|
||||
|
||||
override def cloneType = new RoCCCoreIO()(p).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
/** Base classes for Diplomatic TL2 RoCC units **/
|
||||
@ -64,7 +62,7 @@ abstract class LazyRoCC(implicit p: Parameters) extends LazyModule {
|
||||
val tlNode: TLNode = TLIdentityNode()
|
||||
}
|
||||
|
||||
class RoCCIO(outer: LazyRoCC)(implicit p: Parameters) extends RoCCCoreIO()(p) {
|
||||
class RoCCIO(val outer: LazyRoCC)(implicit p: Parameters) extends RoCCCoreIO()(p) {
|
||||
// Should be handled differently, eventually
|
||||
val ptw = Vec(p(RoccNPTWPorts), new TLBPTWIO)
|
||||
val fpu_req = Decoupled(new FPInput)
|
||||
|
@ -32,18 +32,12 @@ abstract class TLBusWrapper(params: HasTLBusParams, val busName: String)(implici
|
||||
protected def bufferFrom(buffer: BufferParams): TLInwardNode =
|
||||
inwardNode :=* TLBuffer(buffer)
|
||||
|
||||
protected def bufferFrom(buffers: Int): TLInwardNode =
|
||||
TLBuffer.chain(buffers).foldLeft(inwardNode)(_ :=* _)
|
||||
|
||||
protected def fixFrom(policy: TLFIFOFixer.Policy, buffers: Int): TLInwardNode =
|
||||
inwardNode :=* TLBuffer.chain(buffers).foldLeft(TLFIFOFixer(policy))(_ :=* _)
|
||||
protected def fixFrom(policy: TLFIFOFixer.Policy, buffer: BufferParams): TLInwardNode =
|
||||
inwardNode :=* TLBuffer(buffer) :=* TLFIFOFixer(policy)
|
||||
|
||||
protected def bufferTo(buffer: BufferParams): TLOutwardNode =
|
||||
TLBuffer(buffer) :*= delayNode :*= outwardNode
|
||||
|
||||
protected def bufferTo(buffers: Int): TLOutwardNode =
|
||||
TLBuffer.chain(buffers).foldRight(delayNode)(_ :*= _) :*= outwardNode
|
||||
|
||||
protected def fixedWidthTo(buffer: BufferParams): TLOutwardNode =
|
||||
TLWidthWidget(beatBytes) :*= bufferTo(buffer)
|
||||
|
||||
|
@ -35,7 +35,7 @@ trait ExampleModule extends HasRegMap
|
||||
Some(RegFieldDesc("pending", "Pending: Example of a special (W1ToC) Register. " +
|
||||
"Writing a bit here causes it to be reset to 0. " +
|
||||
"The bits are set when the corresponding bit in 'state' is high.",
|
||||
reset=Some(0xF)))))
|
||||
reset=Some(0xF), volatile=true))))
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ class TLRAMModel(log: String = "", ignoreErrorData: Boolean = false)(implicit p:
|
||||
}
|
||||
|
||||
when (a.opcode === TLMessages.Get) {
|
||||
printf(log + " G 0x%x - 0%x\n", a_base, a_base | UIntToOH1(a_size, addressBits))
|
||||
printf(log + " G 0x%x - 0x%x\n", a_base, a_base | UIntToOH1(a_size, addressBits))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,9 +10,6 @@ import freechips.rocketchip.interrupts._
|
||||
import freechips.rocketchip.util.{HeterogeneousBag, ElaborationArtefacts}
|
||||
import scala.math.{min,max}
|
||||
|
||||
import org.json4s.JsonDSL._
|
||||
import org.json4s.jackson.JsonMethods.{pretty, render}
|
||||
|
||||
case class TLRegisterNode(
|
||||
address: Seq[AddressSet],
|
||||
device: Device,
|
||||
@ -85,44 +82,15 @@ case class TLRegisterNode(
|
||||
bundleIn.e.ready := Bool(true)
|
||||
|
||||
// Dump out the register map for documentation purposes.
|
||||
val regDescs = mapping.flatMap { case (offset, seq) =>
|
||||
var currentBitOffset = 0
|
||||
seq.zipWithIndex.map { case (f, i) => {
|
||||
val tmp = (f.desc.map{ _.name}.getOrElse(s"unnamedRegField${offset.toHexString}_${currentBitOffset}") -> (
|
||||
("byteOffset" -> s"0x${offset.toHexString}") ~
|
||||
("bitOffset" -> currentBitOffset) ~
|
||||
("bitWidth" -> f.width) ~
|
||||
("name" -> f.desc.map(_.name)) ~
|
||||
("description" -> f.desc.map{d => if (d.desc == "") None else Some(d.desc)}) ~
|
||||
("resetValue" -> f.desc.map{_.reset}) ~
|
||||
("group" -> f.desc.map{_.group}) ~
|
||||
("groupDesc" -> f.desc.map{_.groupDesc}) ~
|
||||
("accessType" -> f.desc.map {d => d.access.toString}) ~
|
||||
("enumerations" -> f.desc.map {d =>
|
||||
Option(d.enumerations.map { case (key, (name, desc)) =>
|
||||
(("value" -> key) ~
|
||||
("name" -> name) ~
|
||||
("description" -> desc))
|
||||
}).filter(_.nonEmpty)})
|
||||
))
|
||||
currentBitOffset = currentBitOffset + f.width
|
||||
tmp
|
||||
}}
|
||||
}
|
||||
|
||||
//TODO: It would be better to name this other than "Device at ...."
|
||||
val base = s"0x${address.head.base.toInt.toHexString}"
|
||||
val json = ("peripheral" -> (
|
||||
("displayName" -> s"deviceAt${base}") ~
|
||||
("baseAddress" -> base) ~
|
||||
("regfields" -> regDescs)
|
||||
))
|
||||
|
||||
val base = address.head.base
|
||||
val baseHex = s"0x${base.toInt.toHexString}"
|
||||
val name = s"deviceAt${baseHex}" //TODO: It would be better to name this other than "Device at ...."
|
||||
val json = RegMappingAnnotation.serialize(base, name, mapping:_*)
|
||||
var suffix = 0
|
||||
while( ElaborationArtefacts.contains(s"${base}.${suffix}.regmap.json")){
|
||||
while( ElaborationArtefacts.contains(s"${baseHex}.${suffix}.regmap.json")){
|
||||
suffix = suffix + 1
|
||||
}
|
||||
ElaborationArtefacts.add(s"${base}.${suffix}.regmap.json", pretty(render(json)))
|
||||
ElaborationArtefacts.add(s"${baseHex}.${suffix}.regmap.json", json)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,10 +13,16 @@ class TLRAM(
|
||||
cacheable: Boolean = true,
|
||||
executable: Boolean = true,
|
||||
beatBytes: Int = 4,
|
||||
eccBytes: Int = 1,
|
||||
devName: Option[String] = None,
|
||||
errors: Seq[AddressSet] = Nil)
|
||||
errors: Seq[AddressSet] = Nil,
|
||||
code: Code = new IdentityCode)
|
||||
(implicit p: Parameters) extends DiplomaticSRAM(address, beatBytes, devName)
|
||||
{
|
||||
require (eccBytes >= 1 && isPow2(eccBytes))
|
||||
require (beatBytes >= 1 && isPow2(beatBytes))
|
||||
require (eccBytes <= beatBytes, s"TLRAM eccBytes (${eccBytes}) > beatBytes (${beatBytes}). Use a WidthWidget=>Fragmenter=>SRAM if you need high density and narrow ECC; it will do bursts efficiently")
|
||||
|
||||
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||
Seq(TLManagerParameters(
|
||||
address = List(address) ++ errors,
|
||||
@ -33,46 +39,127 @@ class TLRAM(
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val (in, edge) = node.in(0)
|
||||
|
||||
val width = code.width(eccBytes*8)
|
||||
val lanes = beatBytes/eccBytes
|
||||
val addrBits = (mask zip edge.addr_hi(in.a.bits).toBools).filter(_._1).map(_._2)
|
||||
val a_legal = address.contains(in.a.bits.address)
|
||||
val memAddress = Cat(addrBits.reverse)
|
||||
val mem = makeSinglePortedByteWriteSeqMem(1 << addrBits.size)
|
||||
val mem = makeSinglePortedByteWriteSeqMem(1 << addrBits.size, lanes, width)
|
||||
|
||||
/* This block uses a two-stage pipeline; A=>D
|
||||
* Both stages vie for access to the single SRAM port.
|
||||
* Stage D has absolute priority over stage A.
|
||||
* - read-modify-writeback for sub-lane access happens here
|
||||
* - writeback of correctable data happens here
|
||||
* - both actions may occur concurrently
|
||||
* Stage A has lower priority and will stall if blocked
|
||||
* - read operations happen here
|
||||
* - full-lane write operations happen here
|
||||
*/
|
||||
|
||||
// D stage registers from A
|
||||
val d_full = RegInit(Bool(false))
|
||||
val d_read = Reg(Bool())
|
||||
val d_ram_valid = RegInit(Bool(false)) // true if we just read-out from SRAM
|
||||
val d_size = Reg(UInt())
|
||||
val d_source = Reg(UInt())
|
||||
val d_data = Wire(UInt())
|
||||
val d_legal = Reg(Bool())
|
||||
val d_read = Reg(Bool())
|
||||
val d_address = Reg(UInt(width = addrBits.size))
|
||||
val d_rmw_mask = Reg(UInt(width = beatBytes))
|
||||
val d_rmw_data = Reg(UInt(width = 8*beatBytes))
|
||||
|
||||
// Flow control
|
||||
when (in.d.fire()) { d_full := Bool(false) }
|
||||
when (in.a.fire()) { d_full := Bool(true) }
|
||||
in.d.valid := d_full
|
||||
in.a.ready := in.d.ready || !d_full
|
||||
// Decode raw unregistered SRAM output
|
||||
val d_raw_data = Wire(Vec(lanes, Bits(width = width)))
|
||||
val d_decoded = d_raw_data.map(lane => code.decode(lane))
|
||||
val d_corrected = Cat(d_decoded.map(_.corrected).reverse)
|
||||
val d_uncorrected = Cat(d_decoded.map(_.uncorrected).reverse)
|
||||
val d_correctable = d_decoded.map(_.correctable)
|
||||
val d_uncorrectable = d_decoded.map(_.uncorrectable)
|
||||
val d_need_fix = d_correctable.reduce(_ || _)
|
||||
val d_error = d_uncorrectable.reduce(_ || _)
|
||||
|
||||
// What does D-stage want to write-back?
|
||||
val d_wb_data = Vec(Seq.tabulate(beatBytes) { i =>
|
||||
val upd = d_rmw_mask(i)
|
||||
val rmw = d_rmw_data (8*(i+1)-1, 8*i)
|
||||
val fix = d_corrected(8*(i+1)-1, 8*i) // safe to use, because D-stage write-back always wins arbitration
|
||||
Mux(upd, rmw, fix)
|
||||
}.grouped(eccBytes).map(lane => Cat(lane.reverse)).toList)
|
||||
val (d_wb_lanes, d_wb_poison) = Seq.tabulate(lanes) { i =>
|
||||
val upd = d_rmw_mask(eccBytes*(i+1)-1, eccBytes*i)
|
||||
(upd.orR || d_correctable(i),
|
||||
!upd.andR && d_uncorrectable(i)) // sub-lane writes should not correct uncorrectable
|
||||
}.unzip
|
||||
val d_wb = d_rmw_mask.orR || (d_ram_valid && d_need_fix)
|
||||
|
||||
// Extend the validity of SRAM read-out
|
||||
val d_held_data = RegEnable(d_corrected, d_ram_valid)
|
||||
val d_held_error = RegEnable(d_error, d_ram_valid)
|
||||
|
||||
in.d.bits := edge.AccessAck(d_source, d_size, !d_legal)
|
||||
// avoid data-bus Mux
|
||||
in.d.bits.data := d_data
|
||||
in.d.bits.opcode := Mux(d_read, TLMessages.AccessAckData, TLMessages.AccessAck)
|
||||
in.d.bits.param := UInt(0)
|
||||
in.d.bits.size := d_size
|
||||
in.d.bits.source := d_source
|
||||
in.d.bits.sink := UInt(0)
|
||||
// It is safe to use uncorrected data here because of d_pause
|
||||
in.d.bits.data := Mux(d_ram_valid, d_uncorrected, d_held_data)
|
||||
in.d.bits.error := !d_legal || Mux(d_ram_valid, d_error, d_held_error)
|
||||
|
||||
val read = in.a.bits.opcode === TLMessages.Get
|
||||
val rdata = Wire(Vec(beatBytes, Bits(width = 8)))
|
||||
val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) }
|
||||
d_data := Cat(rdata.reverse)
|
||||
// Formulate a response only when SRAM output is unused or correct
|
||||
val d_pause = d_read && d_ram_valid && d_need_fix
|
||||
in.d.valid := d_full && !d_pause
|
||||
in.a.ready := !d_full || (in.d.ready && !d_pause && !d_wb)
|
||||
|
||||
val a_legal = Bool(errors.isEmpty) || address.contains(in.a.bits.address)
|
||||
val a_address = Cat(addrBits.reverse)
|
||||
val a_read = in.a.bits.opcode === TLMessages.Get
|
||||
val a_data = Vec(Seq.tabulate(lanes) { i => in.a.bits.data(eccBytes*8*(i+1)-1, eccBytes*8*i) })
|
||||
|
||||
/*
|
||||
val a_sublane = Seq.tabulate(lanes) { i =>
|
||||
val upd = in.a.bits.mask(eccBytes*(i+1)-1, eccBytes*i)
|
||||
upd.orR && !upd.andR
|
||||
}.reduce(_ || _)
|
||||
*/
|
||||
val a_sublane = if (eccBytes == 1) Bool(false) else
|
||||
in.a.bits.opcode === TLMessages.PutPartialData ||
|
||||
in.a.bits.size < UInt(log2Ceil(eccBytes))
|
||||
val a_ren = a_read || a_sublane
|
||||
val a_lanes = Seq.tabulate(lanes) { i => in.a.bits.mask(eccBytes*(i+1)-1, eccBytes*i).orR }
|
||||
|
||||
when (in.d.fire()) { d_full := Bool(false) }
|
||||
d_ram_valid := Bool(false)
|
||||
d_rmw_mask := UInt(0)
|
||||
when (in.a.fire()) {
|
||||
d_read := read
|
||||
d_full := Bool(true)
|
||||
d_ram_valid := a_ren && a_legal
|
||||
d_size := in.a.bits.size
|
||||
d_source := in.a.bits.source
|
||||
d_legal := a_legal
|
||||
d_read := a_read
|
||||
d_address := a_address
|
||||
d_rmw_mask := UInt(0)
|
||||
when (!a_read && a_sublane) {
|
||||
d_rmw_mask := in.a.bits.mask
|
||||
d_rmw_data := in.a.bits.data
|
||||
}
|
||||
d_held_error:= Bool(false)
|
||||
}
|
||||
|
||||
// exactly this pattern is required to get a RWM memory
|
||||
when (in.a.fire() && !read && a_legal) {
|
||||
mem.write(memAddress, wdata, in.a.bits.mask.toBools)
|
||||
}
|
||||
val ren = in.a.fire() && read
|
||||
rdata := mem.readAndHold(memAddress, ren)
|
||||
// SRAM arbitration
|
||||
val a_fire = in.a.fire() && a_legal
|
||||
val wen = d_wb || (a_fire && !a_ren)
|
||||
// val ren = !d_wb && (a_fire && a_ren)
|
||||
val ren = !wen && a_fire // help Chisel infer a RW-port
|
||||
|
||||
val addr = Mux(d_wb, d_address, a_address)
|
||||
val sel = Mux(d_wb, Vec(d_wb_lanes), Vec(a_lanes))
|
||||
val dat = Mux(d_wb, d_wb_data, a_data)
|
||||
val poison = Mux(d_wb, Vec(d_wb_poison), Vec.fill(lanes) { Bool(false) })
|
||||
val coded = Vec((dat zip poison) map { case (d, p) =>
|
||||
if (code.canDetect) code.encode(d, p) else code.encode(d)
|
||||
})
|
||||
|
||||
d_raw_data := mem.read(addr, ren)
|
||||
when (wen) { mem.write(addr, coded, sel) }
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
@ -88,10 +175,12 @@ object TLRAM
|
||||
cacheable: Boolean = true,
|
||||
executable: Boolean = true,
|
||||
beatBytes: Int = 4,
|
||||
eccBytes: Int = 1,
|
||||
devName: Option[String] = None,
|
||||
errors: Seq[AddressSet] = Nil)(implicit p: Parameters): TLInwardNode =
|
||||
errors: Seq[AddressSet] = Nil,
|
||||
code: Code = new IdentityCode)(implicit p: Parameters): TLInwardNode =
|
||||
{
|
||||
val ram = LazyModule(new TLRAM(address, cacheable, executable, beatBytes, devName, errors))
|
||||
val ram = LazyModule(new TLRAM(address, cacheable, executable, beatBytes, eccBytes, devName, errors, code))
|
||||
ram.node
|
||||
}
|
||||
}
|
||||
@ -115,3 +204,20 @@ class TLRAMSimpleTest(ramBeatBytes: Int, txns: Int = 5000, timeout: Int = 500000
|
||||
val dut = Module(LazyModule(new TLRAMSimple(ramBeatBytes, txns)).module)
|
||||
io.finished := dut.io.finished
|
||||
}
|
||||
|
||||
class TLRAMECC(ramBeatBytes: Int, eccBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule {
|
||||
val fuzz = LazyModule(new TLFuzzer(txns))
|
||||
val model = LazyModule(new TLRAMModel("SRAMSimple"))
|
||||
val ram = LazyModule(new TLRAM(AddressSet(0x0, 0x3ff), beatBytes = ramBeatBytes, eccBytes = eccBytes, code = new SECDEDCode))
|
||||
|
||||
ram.node := TLDelayer(0.25) := model.node := fuzz.node
|
||||
|
||||
lazy val module = new LazyModuleImp(this) with UnitTestModule {
|
||||
io.finished := fuzz.module.io.finished
|
||||
}
|
||||
}
|
||||
|
||||
class TLRAMECCTest(ramBeatBytes: Int, eccBytes: Int, txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) {
|
||||
val dut = Module(LazyModule(new TLRAMECC(ramBeatBytes, eccBytes, txns)).module)
|
||||
io.finished := dut.io.finished
|
||||
}
|
||||
|
@ -9,6 +9,30 @@ import freechips.rocketchip.util._
|
||||
import freechips.rocketchip.amba.axi4._
|
||||
import scala.math.{min, max}
|
||||
|
||||
class TLtoAXI4IdMap(tl: TLClientPortParameters, axi4: AXI4MasterPortParameters) {
|
||||
private val axiDigits = String.valueOf(axi4.endId-1).length()
|
||||
private val tlDigits = String.valueOf(tl.endSourceId-1).length()
|
||||
private val fmt = s"\t[%${axiDigits}d, %${axiDigits}d) <= [%${tlDigits}d, %${tlDigits}d) %s%s%s"
|
||||
private val sorted = tl.clients.sortWith(TLToAXI4.sortByType)
|
||||
|
||||
val mapping: Seq[TLToAXI4IdMapEntry] = (sorted zip axi4.masters) map { case (c, m) =>
|
||||
TLToAXI4IdMapEntry(m.id, c.sourceId, c.name, c.supportsProbe, c.requestFifo)
|
||||
}
|
||||
|
||||
def pretty: String = mapping.map(_.pretty(fmt)).mkString(",\n")
|
||||
}
|
||||
|
||||
case class TLToAXI4IdMapEntry(axi4Id: IdRange, tlId: IdRange, name: String, isCache: Boolean, requestFifo: Boolean) {
|
||||
def pretty(fmt: String) = fmt.format(
|
||||
axi4Id.start,
|
||||
axi4Id.end,
|
||||
tlId.start,
|
||||
tlId.end,
|
||||
s""""$name"""",
|
||||
if (isCache) " [CACHE]" else "",
|
||||
if (requestFifo) " [FIFO]" else "")
|
||||
}
|
||||
|
||||
case class TLToAXI4Node(stripBits: Int = 0)(implicit valName: ValName) extends MixedAdapterNode(TLImp, AXI4Imp)(
|
||||
dFn = { p =>
|
||||
p.clients.foreach { c =>
|
||||
@ -59,32 +83,25 @@ class TLToAXI4(val combinational: Boolean = true, val adapterName: Option[String
|
||||
require (slaves(0).interleavedId.isDefined)
|
||||
slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) }
|
||||
|
||||
val axiDigits = String.valueOf(edgeOut.master.endId-1).length()
|
||||
val tlDigits = String.valueOf(edgeIn.client.endSourceId-1).length()
|
||||
|
||||
// Construct the source=>ID mapping table
|
||||
adapterName.foreach { n => println(s"$n AXI4-ID <= TL-Source mapping:") }
|
||||
val map = new TLtoAXI4IdMap(edgeIn.client, edgeOut.master)
|
||||
val sourceStall = Wire(Vec(edgeIn.client.endSourceId, Bool()))
|
||||
val sourceTable = Wire(Vec(edgeIn.client.endSourceId, out.aw.bits.id))
|
||||
val idStall = Wire(init = Vec.fill(edgeOut.master.endId) { Bool(false) })
|
||||
var idCount = Array.fill(edgeOut.master.endId) { None:Option[Int] }
|
||||
val maps = (edgeIn.client.clients.sortWith(TLToAXI4.sortByType) zip edgeOut.master.masters) flatMap { case (c, m) =>
|
||||
for (i <- 0 until c.sourceId.size) {
|
||||
val id = m.id.start + (if (c.requestFifo) 0 else (i >> stripBits))
|
||||
sourceStall(c.sourceId.start + i) := idStall(id)
|
||||
sourceTable(c.sourceId.start + i) := UInt(id)
|
||||
}
|
||||
if (c.requestFifo) { idCount(m.id.start) = Some(c.sourceId.size) }
|
||||
adapterName.map { n =>
|
||||
val fmt = s"\t[%${axiDigits}d, %${axiDigits}d) <= [%${tlDigits}d, %${tlDigits}d) %s%s"
|
||||
println(fmt.format(m.id.start, m.id.end, c.sourceId.start, c.sourceId.end, c.name, if (c.supportsProbe) " CACHE" else ""))
|
||||
s"""{"axi4-id":[${m.id.start},${m.id.end}],"tilelink-id":[${c.sourceId.start},${c.sourceId.end}],"master":["${c.name}"],"cache":[${!(!c.supportsProbe)}]}"""
|
||||
|
||||
map.mapping.foreach { case TLToAXI4IdMapEntry(axi4Id, tlId, _, _, fifo) =>
|
||||
for (i <- 0 until tlId.size) {
|
||||
val id = axi4Id.start + (if (fifo) 0 else (i >> stripBits))
|
||||
sourceStall(tlId.start + i) := idStall(id)
|
||||
sourceTable(tlId.start + i) := UInt(id)
|
||||
}
|
||||
if (fifo) { idCount(axi4Id.start) = Some(tlId.size) }
|
||||
}
|
||||
|
||||
adapterName.foreach { n =>
|
||||
println("")
|
||||
ElaborationArtefacts.add(s"${n}.axi4.json", s"""{"mapping":[${maps.mkString(",")}]}""")
|
||||
println(s"$n AXI4-ID <= TL-Source mapping:\n${map.pretty}\n")
|
||||
ElaborationArtefacts.add(s"$n.axi4.json", s"""{"mapping":[${map.mapping.mkString(",")}]}""")
|
||||
}
|
||||
|
||||
// We need to keep the following state from A => D: (size, source)
|
||||
|
@ -50,7 +50,10 @@ class WithTLSimpleUnitTests extends Config((site, here, up) => {
|
||||
Module(new TLRR1Test( txns= 3*txns, timeout=timeout)),
|
||||
Module(new TLRAMRationalCrossingTest(txns= 3*txns, timeout=timeout)),
|
||||
Module(new TLRAMAsyncCrossingTest( txns= 5*txns, timeout=timeout)),
|
||||
Module(new TLRAMAtomicAutomataTest( txns=10*txns, timeout=timeout)) ) }
|
||||
Module(new TLRAMAtomicAutomataTest( txns=10*txns, timeout=timeout)),
|
||||
Module(new TLRAMECCTest(8, 4, txns=15*txns, timeout=timeout)),
|
||||
Module(new TLRAMECCTest(4, 1, txns=15*txns, timeout=timeout)),
|
||||
Module(new TLRAMECCTest(1, 1, txns=15*txns, timeout=timeout)) ) }
|
||||
})
|
||||
|
||||
class WithTLWidthUnitTests extends Config((site, here, up) => {
|
||||
|
@ -19,7 +19,12 @@ abstract class Code
|
||||
def canCorrect: Boolean
|
||||
|
||||
def width(w0: Int): Int
|
||||
def encode(x: UInt): UInt
|
||||
|
||||
/** Encode x to a codeword suitable for decode.
|
||||
* If poison is true, the decoded value will report uncorrectable
|
||||
* error despite decoding to the supplied value 'x'.
|
||||
*/
|
||||
def encode(x: UInt, poison: Bool = Bool(false)): UInt
|
||||
def decode(x: UInt): Decoding
|
||||
|
||||
/** Copy the bits in x to the right bit positions in an encoded word,
|
||||
@ -36,7 +41,10 @@ class IdentityCode extends Code
|
||||
def canCorrect = false
|
||||
|
||||
def width(w0: Int) = w0
|
||||
def encode(x: UInt) = x
|
||||
def encode(x: UInt, poison: Bool = Bool(false)) = {
|
||||
require (poison.isLit && poison.litValue == 0, "IdentityCode can not be poisoned")
|
||||
x
|
||||
}
|
||||
def swizzle(x: UInt) = x
|
||||
def decode(y: UInt) = new Decoding {
|
||||
def uncorrected = y
|
||||
@ -52,7 +60,7 @@ class ParityCode extends Code
|
||||
def canCorrect = false
|
||||
|
||||
def width(w0: Int) = w0+1
|
||||
def encode(x: UInt) = Cat(x.xorR, x)
|
||||
def encode(x: UInt, poison: Bool = Bool(false)) = Cat(x.xorR ^ poison, x)
|
||||
def swizzle(x: UInt) = Cat(false.B, x)
|
||||
def decode(y: UInt) = new Decoding {
|
||||
val uncorrected = y(y.getWidth-2,0)
|
||||
@ -67,20 +75,26 @@ class SECCode extends Code
|
||||
def canDetect = true
|
||||
def canCorrect = true
|
||||
|
||||
// SEC codes may or may not be poisonous depending on the length
|
||||
// If the code is perfect, every non-codeword is correctable
|
||||
def poisonous(n: Int) = !isPow2(n+1)
|
||||
|
||||
def width(k: Int) = {
|
||||
val m = log2Floor(k) + 1
|
||||
k + m + (if((1 << m) < m+k+1) 1 else 0)
|
||||
}
|
||||
def encode(x: UInt) = {
|
||||
def encode(x: UInt, poison: Bool = Bool(false)) = {
|
||||
val k = x.getWidth
|
||||
require(k > 0)
|
||||
val n = width(k)
|
||||
|
||||
require ((poison.isLit && poison.litValue == 0) || poisonous(n), s"SEC code of length ${n} cannot be poisoned")
|
||||
|
||||
val y = for (i <- 1 to n) yield {
|
||||
if (isPow2(i)) {
|
||||
val r = for (j <- 1 to n; if j != i && (j & i) != 0)
|
||||
yield x(mapping(j))
|
||||
r reduce (_^_)
|
||||
r.reduce(_^_) ^ poison
|
||||
} else
|
||||
x(mapping(i))
|
||||
}
|
||||
@ -106,7 +120,7 @@ class SECCode extends Code
|
||||
val uncorrected = swizzle(y)
|
||||
val corrected = swizzle(((y << 1) ^ UIntToOH(syndrome)) >> 1)
|
||||
val correctable = syndrome.orR
|
||||
val uncorrectable = syndrome > UInt(n)
|
||||
val uncorrectable = if (poisonous(n)) { syndrome > UInt(n) } else { Bool(false) }
|
||||
}
|
||||
private def mapping(i: Int) = i-1-log2Up(i)
|
||||
}
|
||||
@ -120,7 +134,12 @@ class SECDEDCode extends Code
|
||||
private val par = new ParityCode
|
||||
|
||||
def width(k: Int) = sec.width(k)+1
|
||||
def encode(x: UInt) = par.encode(sec.encode(x))
|
||||
def encode(x: UInt, poison: Bool = Bool(false)) = {
|
||||
// toggling two bits ensures the error is uncorrectable
|
||||
val toggle_lo = poison.asUInt // a non-data bit in SEC
|
||||
val toggle_hi = toggle_lo << sec.width(x.getWidth) // the parity bit
|
||||
par.encode(sec.encode(x)) ^ toggle_lo ^ toggle_hi
|
||||
}
|
||||
def swizzle(x: UInt) = par.swizzle(sec.swizzle(x))
|
||||
def decode(x: UInt) = new Decoding {
|
||||
val secdec = sec.decode(x(x.getWidth-2,0))
|
||||
@ -178,3 +197,15 @@ trait CanHaveErrors extends Bundle {
|
||||
val correctable: Option[ValidIO[UInt]]
|
||||
val uncorrectable: Option[ValidIO[UInt]]
|
||||
}
|
||||
|
||||
object Code {
|
||||
def fromString(s: Option[String]): Code = fromString(s.getOrElse("none"))
|
||||
def fromString(s: String): Code = s.toLowerCase match {
|
||||
case "none" => new IdentityCode
|
||||
case "identity" => new IdentityCode
|
||||
case "parity" => new ParityCode
|
||||
case "sec" => new SECCode
|
||||
case "secded" => new SECDEDCode
|
||||
case _ => throw new IllegalArgumentException("Unknown ECC type")
|
||||
}
|
||||
}
|
||||
|
@ -10,8 +10,7 @@ import freechips.rocketchip.system.{TestGeneration, DefaultTestSuites}
|
||||
import freechips.rocketchip.config._
|
||||
import freechips.rocketchip.diplomacy.LazyModule
|
||||
import java.io.{File, FileWriter}
|
||||
import net.jcazevedo.moultingyaml._
|
||||
import firrtl.annotations.AnnotationYamlProtocol._
|
||||
import firrtl.annotations.JsonProtocol
|
||||
|
||||
/** Representation of the information this Generator needs to collect from external sources. */
|
||||
case class ParsedInputNames(
|
||||
@ -99,9 +98,9 @@ trait GeneratorApp extends App with HasGeneratorUtilities {
|
||||
}
|
||||
|
||||
def generateAnno {
|
||||
val annotationFile = new File(td, s"$longName.anno")
|
||||
val annotationFile = new File(td, s"$longName.anno.json")
|
||||
val af = new FileWriter(annotationFile)
|
||||
af.write(circuit.annotations.toArray.toYaml.prettyPrint)
|
||||
af.write(JsonProtocol.serialize(circuit.annotations.map(_.toFirrtl)))
|
||||
af.close()
|
||||
}
|
||||
|
||||
|
@ -4,21 +4,18 @@
|
||||
package freechips.rocketchip.util
|
||||
|
||||
import Chisel._
|
||||
import chisel3.experimental.{ChiselAnnotation, RawModule}
|
||||
import chisel3.experimental.RawModule
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import scala.math._
|
||||
|
||||
class ParameterizedBundle(implicit p: Parameters) extends Bundle
|
||||
|
||||
// TODO: replace this with an implicit class when @chisel unprotects dontTouchPorts
|
||||
trait DontTouch {
|
||||
self: RawModule =>
|
||||
trait DontTouch { self: RawModule =>
|
||||
|
||||
def dontTouch(data: Data): Unit = data match {
|
||||
case agg: Aggregate =>
|
||||
agg.getElements.foreach(dontTouch)
|
||||
case elt: Element =>
|
||||
annotate(ChiselAnnotation(elt, classOf[firrtl.Transform], "DONTtouch!"))
|
||||
case agg: Aggregate => agg.getElements.foreach(dontTouch)
|
||||
case elt: Element => chisel3.core.dontTouch(elt)
|
||||
}
|
||||
|
||||
/** Marks every port as don't touch
|
||||
|
@ -57,10 +57,6 @@ VCS_OPTS = -notice -line +lint=all,noVCDE,noONGS,noUI -error=PCWM-L -timescale=1
|
||||
+define+RANDOMIZE_INVALID_ASSIGN \
|
||||
+libext+.v \
|
||||
|
||||
VCS_OPTS += +vpi
|
||||
VCS_OPTS += -CC "-DVCS_VPI"
|
||||
|
||||
|
||||
#--------------------------------------------------------------------
|
||||
# Build the simulator
|
||||
#--------------------------------------------------------------------
|
||||
@ -84,5 +80,5 @@ $(simv_debug) : $(sim_vsrcs) $(sim_csrcs)
|
||||
#--------------------------------------------------------------------
|
||||
|
||||
seed = $(shell date +%s)
|
||||
exec_simv = $(simv) -q +ntb_random_seed_automatic
|
||||
exec_simv_debug = $(simv_debug) -q +ntb_random_seed_automatic
|
||||
exec_simv = $(simv) +permissive -q +ntb_random_seed_automatic +permissive-off
|
||||
exec_simv_debug = $(simv_debug) +permissive -q +ntb_random_seed_automatic +permissive-off
|
||||
|
@ -14,7 +14,7 @@ $(generated_dir)/%.fir $(generated_dir)/%.d: $(FIRRTL_JAR) $(chisel_srcs) $(boot
|
||||
|
||||
$(generated_dir)/%.v $(generated_dir)/%.conf: $(generated_dir)/%.fir $(FIRRTL_JAR)
|
||||
mkdir -p $(dir $@)
|
||||
$(FIRRTL) -i $< -o $(generated_dir)/$*.v -X verilog --infer-rw $(MODEL) --repl-seq-mem -c:$(MODEL):-o:$(generated_dir)/$*.conf -faf $(generated_dir)/$*.anno
|
||||
$(FIRRTL) -i $< -o $(generated_dir)/$*.v -X verilog --infer-rw $(MODEL) --repl-seq-mem -c:$(MODEL):-o:$(generated_dir)/$*.conf -faf $(generated_dir)/$*.anno.json
|
||||
|
||||
$(generated_dir)/$(long_name).behav_srams.v : $(generated_dir)/$(long_name).conf $(mem_gen)
|
||||
cd $(generated_dir) && \
|
||||
@ -28,19 +28,19 @@ $(generated_dir)/$(long_name).behav_srams.v : $(generated_dir)/$(long_name).conf
|
||||
.PRECIOUS: $(output_dir)/%.vpd
|
||||
|
||||
$(output_dir)/%.run: $(output_dir)/% $(simv)
|
||||
cd $(sim_dir) && $(exec_simv) +max-cycles=$(timeout_cycles) $< 2> /dev/null 2> $@ && [ $$PIPESTATUS -eq 0 ]
|
||||
cd $(sim_dir) && $(exec_simv) +permissive +max-cycles=$(timeout_cycles) +permissive-off $< 2> /dev/null 2> $@ && [ $$PIPESTATUS -eq 0 ]
|
||||
|
||||
$(output_dir)/%.out: $(output_dir)/% $(simv)
|
||||
cd $(sim_dir) && $(exec_simv) +verbose +max-cycles=$(timeout_cycles) $< $(disasm) $@ && [ $$PIPESTATUS -eq 0 ]
|
||||
cd $(sim_dir) && $(exec_simv) +permissive +verbose +max-cycles=$(timeout_cycles) +permissive-off $< $(disasm) $@ && [ $$PIPESTATUS -eq 0 ]
|
||||
|
||||
$(output_dir)/%.vcd: $(output_dir)/% $(simv_debug)
|
||||
cd $(sim_dir) && $(exec_simv_debug) +verbose +vcdfile=$@ +max-cycles=$(timeout_cycles) $< $(disasm) $(patsubst %.vcd,%.out,$@) && [ $$PIPESTATUS -eq 0 ]
|
||||
cd $(sim_dir) && $(exec_simv_debug) +permissive +verbose +vcdfile=$@ +max-cycles=$(timeout_cycles) +permissive-off $< $(disasm) $(patsubst %.vcd,%.out,$@) && [ $$PIPESTATUS -eq 0 ]
|
||||
|
||||
$(output_dir)/%.vpd: $(output_dir)/% $(simv_debug)
|
||||
cd $(sim_dir) && $(exec_simv_debug) +verbose +vcdplusfile=$@ +max-cycles=$(timeout_cycles) $< $(disasm) $(patsubst %.vpd,%.out,$@) && [ $$PIPESTATUS -eq 0 ]
|
||||
cd $(sim_dir) && $(exec_simv_debug) +permissive +verbose +vcdplusfile=$@ +max-cycles=$(timeout_cycles) +permissive-off $< $(disasm) $(patsubst %.vpd,%.out,$@) && [ $$PIPESTATUS -eq 0 ]
|
||||
|
||||
$(output_dir)/%.saif: $(output_dir)/% $(simv_debug)
|
||||
cd $(sim_dir) && rm -f $(output_dir)/pipe-$*.vcd && vcd2saif -input $(output_dir)/pipe-$*.vcd -pipe "$(exec_simv_debug) +verbose +vcdfile=$(output_dir)/pipe-$*.vcd +max-cycles=$(bmark_timeout_cycles) $<" -output $@ > $(patsubst %.saif,%.out,$@) 2>&1
|
||||
cd $(sim_dir) && rm -f $(output_dir)/pipe-$*.vcd && vcd2saif -input $(output_dir)/pipe-$*.vcd -pipe "$(exec_simv_debug) +permissive +verbose +vcdfile=$(output_dir)/pipe-$*.vcd +max-cycles=$(bmark_timeout_cycles) +permissive-off $<" -output $@ > $(patsubst %.saif,%.out,$@) 2>&1
|
||||
|
||||
run: run-asm-tests run-bmark-tests
|
||||
run-debug: run-asm-tests-debug run-bmark-tests-debug
|
||||
|
@ -49,6 +49,8 @@ module SimJTAG #(
|
||||
bit __jtag_TRSTn;
|
||||
int __exit;
|
||||
|
||||
reg init_done_sticky;
|
||||
|
||||
assign #0.1 jtag_TCK = __jtag_TCK;
|
||||
assign #0.1 jtag_TMS = __jtag_TMS;
|
||||
assign #0.1 jtag_TDI = __jtag_TDI;
|
||||
@ -61,8 +63,10 @@ module SimJTAG #(
|
||||
if (reset || r_reset) begin
|
||||
__exit = 0;
|
||||
tickCounterReg <= TICK_DELAY;
|
||||
init_done_sticky <= 1'b0;
|
||||
end else begin
|
||||
if (enable && init_done) begin
|
||||
init_done_sticky <= init_done | init_done_sticky;
|
||||
if (enable && init_done_sticky) begin
|
||||
tickCounterReg <= tickCounterNxt;
|
||||
if (tickCounterReg == 0) begin
|
||||
__exit = jtag_tick(
|
||||
@ -72,7 +76,7 @@ module SimJTAG #(
|
||||
__jtag_TRSTn,
|
||||
__jtag_TDO);
|
||||
end
|
||||
end // if (enable && init_done)
|
||||
end // if (enable && init_done_sticky)
|
||||
end // else: !if(reset || r_reset)
|
||||
end // always @ (posedge clock)
|
||||
|
||||
|
@ -18,6 +18,7 @@ module TestDriver;
|
||||
reg [63:0] max_cycles = 0;
|
||||
reg [63:0] dump_start = 0;
|
||||
reg [63:0] trace_count = 0;
|
||||
reg [1023:0] fsdbfile = 0;
|
||||
reg [1023:0] vcdplusfile = 0;
|
||||
reg [1023:0] vcdfile = 0;
|
||||
int unsigned rand_value;
|
||||
@ -50,7 +51,19 @@ module TestDriver;
|
||||
`ifdef VCS
|
||||
$vcdplusfile(vcdplusfile);
|
||||
`else
|
||||
$fdisplay(stderr, "Error: +vcdplusfile is VCS-only; use +vcdfile instead");
|
||||
$fdisplay(stderr, "Error: +vcdplusfile is VCS-only; use +vcdfile instead or recompile with VCS=1");
|
||||
$fatal;
|
||||
`endif
|
||||
end
|
||||
|
||||
if ($value$plusargs("fsdbfile=%s", fsdbfile))
|
||||
begin
|
||||
`ifdef FSDB
|
||||
$fsdbDumpfile(fsdbfile);
|
||||
$fsdbDumpvars("+all");
|
||||
//$fsdbDumpSVA;
|
||||
`else
|
||||
$fdisplay(stderr, "Error: +fsdbfile is FSDB-only; use +vcdfile/+vcdplus instead or recompile with FSDB=1");
|
||||
$fatal;
|
||||
`endif
|
||||
end
|
||||
@ -60,7 +73,11 @@ module TestDriver;
|
||||
$dumpfile(vcdfile);
|
||||
$dumpvars(0, testHarness);
|
||||
end
|
||||
`ifdef VCS
|
||||
|
||||
`ifdef FSDB
|
||||
`define VCDPLUSON $fsdbDumpon;
|
||||
`define VCDPLUSCLOSE $fsdbDumpoff;
|
||||
`elsif VCS
|
||||
`define VCDPLUSON $vcdpluson(0); $vcdplusmemon(0);
|
||||
`define VCDPLUSCLOSE $vcdplusclose; $dumpoff;
|
||||
`else
|
||||
@ -72,9 +89,9 @@ module TestDriver;
|
||||
`define VCDPLUSON
|
||||
`define VCDPLUSCLOSE
|
||||
|
||||
if ($test$plusargs("vcdplusfile=") || $test$plusargs("vcdfile="))
|
||||
if ($test$plusargs("vcdplusfile=") || $test$plusargs("vcdfile=") || $test$plusargs("fsdbfile="))
|
||||
begin
|
||||
$fdisplay(stderr, "Error: +vcdfile or +vcdplusfile requested but compile did not have +define+DEBUG enabled");
|
||||
$fdisplay(stderr, "Error: +vcdfile, +vcdplusfile, or +fsdbfile requested but compile did not have +define+DEBUG enabled");
|
||||
$fatal;
|
||||
end
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
$send_result_to_server call=send_result_to_server acc=rw:*
|
||||
$check_for_command call=check_for_command acc=rw:*
|
301
vsrc/jtag_vpi.v
301
vsrc/jtag_vpi.v
@ -1,301 +0,0 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
/*
|
||||
* TCP/IP controlled VPI JTAG Interface.
|
||||
* Based on Julius Baxter's work on jp_vpi.c
|
||||
*
|
||||
* Copyright (C) 2012 Franck Jullien, <franck.jullien@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the names of the copyright holders nor the names of any
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
//VCS coverage exclude_file
|
||||
`define CMD_RESET 0
|
||||
`define CMD_TMS_SEQ 1
|
||||
`define CMD_SCAN_CHAIN 2
|
||||
`define CMD_SCAN_CHAIN_FLIP_TMS 3
|
||||
`define CMD_STOP_SIMU 4
|
||||
|
||||
module JTAGVPI
|
||||
#( parameter DEBUG_INFO = 0,
|
||||
parameter TP = 1,
|
||||
parameter TCK_HALF_PERIOD = 2,// 50, // Clock half period (Clock period = 100 ns => 10 MHz)
|
||||
parameter CMD_DELAY = 2, // 1000
|
||||
parameter INIT_DELAY = 200
|
||||
)
|
||||
(
|
||||
output jtag_TMS,
|
||||
output jtag_TCK,
|
||||
output jtag_TDI,
|
||||
input jtag_TDO_data,
|
||||
input jtag_TDO_driven,
|
||||
|
||||
input enable,
|
||||
input init_done);
|
||||
|
||||
reg tms;
|
||||
reg tck;
|
||||
reg tdi;
|
||||
wire tdo;
|
||||
|
||||
assign jtag_TMS = tms;
|
||||
assign jtag_TCK = tck;
|
||||
assign jtag_TDI = tdi;
|
||||
assign tdo = jtag_TDO_driven ? jtag_TDO_data : 1'bz;
|
||||
|
||||
integer cmd;
|
||||
integer length;
|
||||
integer nb_bits;
|
||||
|
||||
reg [31:0] buffer_out [0:4095]; // Data storage from the jtag server
|
||||
reg [31:0] buffer_in [0:4095]; // Data storage to the jtag server
|
||||
|
||||
integer flip_tms;
|
||||
|
||||
reg [31:0] data_out;
|
||||
reg [31:0] data_in;
|
||||
|
||||
integer debug;
|
||||
|
||||
initial
|
||||
begin
|
||||
tck <= #TP 1'b0;
|
||||
tdi <= #TP 1'bz;
|
||||
tms <= #TP 1'b0;
|
||||
|
||||
data_out <= 32'h0;
|
||||
data_in <= 32'h0;
|
||||
|
||||
// Small delay to get past reset instability
|
||||
// before checking for init_done
|
||||
#INIT_DELAY
|
||||
|
||||
wait(init_done)
|
||||
if($test$plusargs("jtag_vpi_enable")) main;
|
||||
end
|
||||
|
||||
task main;
|
||||
begin
|
||||
$display("JTAG debug module with VPI interface enabled\n");
|
||||
|
||||
reset_tap;
|
||||
goto_run_test_idle_from_reset;
|
||||
|
||||
while (1) begin
|
||||
|
||||
// Check for incoming command
|
||||
// wait until a command is sent
|
||||
// poll with a delay here
|
||||
cmd = -1;
|
||||
|
||||
while (cmd == -1)
|
||||
begin
|
||||
#CMD_DELAY $check_for_command(cmd, length, nb_bits, buffer_out);
|
||||
end
|
||||
|
||||
// now switch on the command
|
||||
case (cmd)
|
||||
|
||||
`CMD_RESET :
|
||||
begin
|
||||
if (DEBUG_INFO)
|
||||
$display("%t ----> CMD_RESET %h\n", $time, length);
|
||||
reset_tap;
|
||||
goto_run_test_idle_from_reset;
|
||||
end
|
||||
|
||||
`CMD_TMS_SEQ :
|
||||
begin
|
||||
if (DEBUG_INFO)
|
||||
$display("%t ----> CMD_TMS_SEQ\n", $time);
|
||||
do_tms_seq;
|
||||
end
|
||||
|
||||
`CMD_SCAN_CHAIN :
|
||||
begin
|
||||
if (DEBUG_INFO)
|
||||
$display("%t ----> CMD_SCAN_CHAIN\n", $time);
|
||||
flip_tms = 0;
|
||||
do_scan_chain;
|
||||
$send_result_to_server(length, buffer_in);
|
||||
end
|
||||
|
||||
`CMD_SCAN_CHAIN_FLIP_TMS :
|
||||
begin
|
||||
if(DEBUG_INFO)
|
||||
$display("%t ----> CMD_SCAN_CHAIN\n", $time);
|
||||
flip_tms = 1;
|
||||
do_scan_chain;
|
||||
$send_result_to_server(length, buffer_in);
|
||||
end
|
||||
|
||||
`CMD_STOP_SIMU :
|
||||
begin
|
||||
if(DEBUG_INFO)
|
||||
$display("%t ----> End of simulation\n", $time);
|
||||
$finish();
|
||||
end
|
||||
|
||||
default:
|
||||
begin
|
||||
$display("Somehow got to the default case in the command case statement.");
|
||||
$display("Command was: %x", cmd);
|
||||
$display("Exiting...");
|
||||
$finish();
|
||||
end
|
||||
|
||||
endcase // case (cmd)
|
||||
|
||||
end // while (1)
|
||||
end
|
||||
|
||||
endtask // main
|
||||
|
||||
|
||||
// Generation of the TCK signal
|
||||
task gen_clk;
|
||||
input [31:0] number;
|
||||
integer i;
|
||||
|
||||
begin
|
||||
for (i = 0; i < number; i = i + 1)
|
||||
begin
|
||||
#TCK_HALF_PERIOD tck <= 1;
|
||||
#TCK_HALF_PERIOD tck <= 0;
|
||||
end
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
// TAP reset
|
||||
task reset_tap;
|
||||
begin
|
||||
if (DEBUG_INFO)
|
||||
$display("(%0t) Task reset_tap", $time);
|
||||
tms <= #1 1'b1;
|
||||
gen_clk(5);
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
|
||||
// Goes to RunTestIdle state
|
||||
task goto_run_test_idle_from_reset;
|
||||
begin
|
||||
if (DEBUG_INFO)
|
||||
$display("(%0t) Task goto_run_test_idle_from_reset", $time);
|
||||
tms <= #1 1'b0;
|
||||
gen_clk(1);
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
//
|
||||
task do_tms_seq;
|
||||
|
||||
integer i,j;
|
||||
reg [31:0] data;
|
||||
integer nb_bits_rem;
|
||||
integer nb_bits_in_this_byte;
|
||||
|
||||
begin
|
||||
if (DEBUG_INFO)
|
||||
$display("(%0t) Task do_tms_seq of %d bits (length = %d)", $time, nb_bits, length);
|
||||
|
||||
// Number of bits to send in the last byte
|
||||
nb_bits_rem = nb_bits % 8;
|
||||
|
||||
for (i = 0; i < length; i = i + 1)
|
||||
begin
|
||||
// If we are in the last byte, we have to send only
|
||||
// nb_bits_rem bits. If not, we send the whole byte.
|
||||
nb_bits_in_this_byte = (i == (length - 1)) ? nb_bits_rem : 8;
|
||||
|
||||
data = buffer_out[i];
|
||||
for (j = 0; j < nb_bits_in_this_byte; j = j + 1)
|
||||
begin
|
||||
tms <= #1 1'b0;
|
||||
if (data[j] == 1) begin
|
||||
tms <= #1 1'b1;
|
||||
end
|
||||
gen_clk(1);
|
||||
end
|
||||
end
|
||||
|
||||
tms <= #1 1'b0;
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
|
||||
//
|
||||
task do_scan_chain;
|
||||
|
||||
integer _bit;
|
||||
integer nb_bits_rem;
|
||||
integer nb_bits_in_this_byte;
|
||||
integer index;
|
||||
|
||||
begin
|
||||
if(DEBUG_INFO)
|
||||
$display("(%0t) Task do_scan_chain of %d bits (length = %d)", $time, nb_bits, length);
|
||||
|
||||
// Number of bits to send in the last byte
|
||||
nb_bits_rem = nb_bits % 8;
|
||||
|
||||
for (index = 0; index < length; index = index + 1)
|
||||
begin
|
||||
// If we are in the last byte, we have to send only
|
||||
// nb_bits_rem bits if it's not zero.
|
||||
// If not, we send the whole byte.
|
||||
nb_bits_in_this_byte = (index == (length - 1)) ? ((nb_bits_rem == 0) ? 8 : nb_bits_rem) : 8;
|
||||
|
||||
data_out = buffer_out[index];
|
||||
for (_bit = 0; _bit < nb_bits_in_this_byte; _bit = _bit + 1)
|
||||
begin
|
||||
tdi <= 1'b0;
|
||||
if (data_out[_bit] == 1'b1) begin
|
||||
tdi <= 1'b1;
|
||||
end
|
||||
|
||||
// On the last bit, set TMS to '1'
|
||||
if (((_bit == (nb_bits_in_this_byte - 1)) && (index == (length - 1))) && (flip_tms == 1)) begin
|
||||
tms <= 1'b1;
|
||||
end
|
||||
|
||||
#TCK_HALF_PERIOD tck <= 1;
|
||||
data_in[_bit] <= tdo;
|
||||
#TCK_HALF_PERIOD tck <= 0;
|
||||
end
|
||||
buffer_in[index] = data_in;
|
||||
end
|
||||
|
||||
tdi <= 1'b0;
|
||||
tms <= 1'b0;
|
||||
end
|
||||
|
||||
endtask
|
||||
|
||||
endmodule
|
Reference in New Issue
Block a user