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,
|
traceLevel := 15,
|
||||||
scalacOptions ++= Seq("-deprecation","-unchecked"),
|
scalacOptions ++= Seq("-deprecation","-unchecked"),
|
||||||
libraryDependencies ++= Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value),
|
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)
|
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"
|
#include "verilated.h"
|
||||||
#if VM_TRACE
|
#if VM_TRACE
|
||||||
|
#include <memory>
|
||||||
#include "verilated_vcd_c.h"
|
#include "verilated_vcd_c.h"
|
||||||
#endif
|
#endif
|
||||||
#include <fesvr/dtm.h>
|
#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)
|
%.v %.conf: %.fir $(FIRRTL_JAR)
|
||||||
mkdir -p $(dir $@)
|
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)
|
$(generated_dir)/$(long_name).behav_srams.v : $(generated_dir)/$(long_name).conf $(VLSI_MEM_GEN)
|
||||||
cd $(generated_dir) && \
|
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}];
|
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;
|
integer i;
|
||||||
initial begin
|
initial begin
|
||||||
`ifdef RANDOMIZE
|
`ifdef RANDOMIZE
|
||||||
@ -35,10 +32,7 @@ module {name}(
|
|||||||
end
|
end
|
||||||
`endif
|
`endif
|
||||||
`endif
|
`endif
|
||||||
if (!$value$plusargs("maskromhex=%s", path)) begin
|
$readmemh("{rom_hex_file}", rom);
|
||||||
path = "{rom_hex_file}";
|
|
||||||
end
|
|
||||||
$readmemh(path, rom);
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -30,8 +30,6 @@ object DMIConsts{
|
|||||||
// This is used outside this block
|
// This is used outside this block
|
||||||
// to indicate 'busy'.
|
// to indicate 'busy'.
|
||||||
def dmi_RESP_RESERVED = "b11".U
|
def dmi_RESP_RESERVED = "b11".U
|
||||||
|
|
||||||
def dmi_haltStatusAddr = 0x40
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object DsbBusConsts {
|
object DsbBusConsts {
|
||||||
@ -254,7 +252,8 @@ object WNotifyWire {
|
|||||||
set := valid
|
set := valid
|
||||||
value := data
|
value := data
|
||||||
Bool(true)
|
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 {
|
} .otherwise {
|
||||||
when (DMCONTROLWrEn) {
|
when (DMCONTROLWrEn) {
|
||||||
DMCONTROLNxt.ndmreset := DMCONTROLWrData.ndmreset
|
DMCONTROLNxt.ndmreset := DMCONTROLWrData.ndmreset
|
||||||
DMCONTROLNxt.hartsel := DMCONTROLWrData.hartsel
|
DMCONTROLNxt.hartsello := DMCONTROLWrData.hartsello
|
||||||
DMCONTROLNxt.haltreq := DMCONTROLWrData.haltreq
|
DMCONTROLNxt.haltreq := DMCONTROLWrData.haltreq
|
||||||
DMCONTROLNxt.resumereq := DMCONTROLWrData.resumereq
|
DMCONTROLNxt.resumereq := DMCONTROLWrData.resumereq
|
||||||
DMCONTROLNxt.ackhavereset := DMCONTROLWrData.ackhavereset
|
DMCONTROLNxt.ackhavereset := DMCONTROLWrData.ackhavereset
|
||||||
@ -375,14 +374,14 @@ class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyMod
|
|||||||
when (~dmactive) {
|
when (~dmactive) {
|
||||||
debugIntNxt(component) := false.B
|
debugIntNxt(component) := false.B
|
||||||
}. otherwise {
|
}. otherwise {
|
||||||
when (DMCONTROLWrEn && DMCONTROLWrData.hartsel === component.U) {
|
when (DMCONTROLWrEn && DMCONTROLWrData.hartsello === component.U) {
|
||||||
debugIntNxt(component) := DMCONTROLWrData.haltreq
|
debugIntNxt(component) := DMCONTROLWrData.haltreq
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
io.innerCtrl.valid := DMCONTROLWrEn
|
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.resumereq := DMCONTROLWrData.resumereq
|
||||||
io.innerCtrl.bits.ackhavereset := DMCONTROLWrData.ackhavereset
|
io.innerCtrl.bits.ackhavereset := DMCONTROLWrData.ackhavereset
|
||||||
|
|
||||||
@ -559,7 +558,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
|||||||
HARTINFORdData.dataaddr := DsbRegAddrs.DATA.U
|
HARTINFORdData.dataaddr := DsbRegAddrs.DATA.U
|
||||||
HARTINFORdData.nscratch := cfg.nScratch.U
|
HARTINFORdData.nscratch := cfg.nScratch.U
|
||||||
|
|
||||||
//----HALTSUM (and halted registers)
|
//----HALTSUM*
|
||||||
val numHaltedStatus = ((nComponents - 1) / 32) + 1
|
val numHaltedStatus = ((nComponents - 1) / 32) + 1
|
||||||
val haltedStatus = Wire(Vec(numHaltedStatus, Bits(width = 32)))
|
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 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
|
//----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", ""))),
|
(DMI_DMSTATUS << 2) -> Seq(RegField.r(32, DMSTATUSRdData.asUInt(), RegFieldDesc("dmi_dmstatus", ""))),
|
||||||
//TODO (DMI_CFGSTRADDR0 << 2) -> cfgStrAddrFields,
|
//TODO (DMI_CFGSTRADDR0 << 2) -> cfgStrAddrFields,
|
||||||
(DMI_HARTINFO << 2) -> Seq(RegField.r(32, HARTINFORdData.asUInt(), RegFieldDesc("dmi_hartinfo", "" /*, reset=Some(HARTINFORdData.litValue)*/))),
|
(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,
|
(DMI_ABSTRACTCS << 2) -> Seq(RWNotify(32, ABSTRACTCSRdData.asUInt(), ABSTRACTCSWrDataVal, ABSTRACTCSRdEn, ABSTRACTCSWrEnMaybe,
|
||||||
Some(RegFieldDesc("dmi_abstractcs", "" /*, reset=Some(ABSTRACTCSReset.litValue)*/)))),
|
Some(RegFieldDesc("dmi_abstractcs", "" /*, reset=Some(ABSTRACTCSReset.litValue)*/)))),
|
||||||
(DMI_ABSTRACTAUTO<< 2) -> Seq(RWNotify(32, ABSTRACTAUTORdData.asUInt(), ABSTRACTAUTOWrDataVal, ABSTRACTAUTORdEn, ABSTRACTAUTOWrEnMaybe,
|
(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),
|
(DMI_PROGBUF0 << 2) -> RegFieldGroup("dmi_progbuf", None, programBufferMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, programBufferNxt(i),
|
||||||
dmiProgramBufferRdEn(i),
|
dmiProgramBufferRdEn(i),
|
||||||
dmiProgramBufferWrEnMaybe(i),
|
dmiProgramBufferWrEnMaybe(i),
|
||||||
Some(RegFieldDesc(s"dmi_progbuf_$i", "", reset = Some(0))))}),
|
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", ""))})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
abstractDataMem.zipWithIndex.foreach { case (x, i) =>
|
abstractDataMem.zipWithIndex.foreach { case (x, i) =>
|
||||||
@ -784,7 +788,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
|||||||
val go = Bool()
|
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),
|
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.");
|
"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
|
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", ""))}),
|
programBufferMem.zipWithIndex.map {case (x, i) => RegField(8, x, RegFieldDesc(s"debug_progbuf_$i", ""))}),
|
||||||
|
|
||||||
// These sections are read-only.
|
// 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},
|
IMPEBREAK(cfg)-> {if (cfg.hasImplicitEbreak) Seq(RegField.r(32, Instructions.EBREAK.value.U,
|
||||||
WHERETO -> Seq(RegField.r(32, jalAbstract.asUInt, RegFieldDesc("debug_whereto", "Instruction filled in by Debug Module to control hart in Debug Mode"))),
|
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"),
|
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 -> 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"),
|
ROMBASE -> RegFieldGroup("debug_rom", Some("Debug ROM"),
|
||||||
DebugRomContents().zipWithIndex.map{case (x, i) => RegField.r(8, (x & 0xFF).U(8.W),
|
DebugRomContents().zipWithIndex.map{case (x, i) => RegField.r(8, (x & 0xFF).U(8.W), RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
|
||||||
RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Override System Bus accesses with dmactive reset.
|
// Override System Bus accesses with dmactive reset.
|
||||||
@ -938,7 +942,7 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
|
|||||||
//------------------------
|
//------------------------
|
||||||
// DMI Register Control and Status
|
// DMI Register Control and Status
|
||||||
|
|
||||||
abstractCommandBusy := (ctrlStateReg != CtrlState(Waiting))
|
abstractCommandBusy := (ctrlStateReg =/= CtrlState(Waiting))
|
||||||
|
|
||||||
ABSTRACTCSWrEnLegal := (ctrlStateReg === CtrlState(Waiting))
|
ABSTRACTCSWrEnLegal := (ctrlStateReg === CtrlState(Waiting))
|
||||||
COMMANDWrEnLegal := (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,
|
// 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
|
// as we haven't implemented Bus Masters or Serial Ports, which are the only cases errors
|
||||||
// can occur.
|
// 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(!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.");
|
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)
|
val dtm = Module(new SimDTM).connect(c, r, d, out)
|
||||||
}
|
}
|
||||||
debug.systemjtag.foreach { sj =>
|
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, c, r, ~r, out)
|
||||||
val jtag = Module(new SimJTAG(tickDelay=3)).connect(sj.jtag, sj.reset, c, r, out)
|
sj.reset := r
|
||||||
sj.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W)
|
sj.mfr_id := p(JtagDTMKey).idcodeManufId.U(11.W)
|
||||||
}
|
}
|
||||||
debug.psd.foreach { _ <> psd }
|
debug.psd.foreach { _ <> psd }
|
||||||
@ -120,15 +120,14 @@ class SimJTAG(tickDelay: Int = 50) extends BlackBox(Map("TICK_DELAY" -> IntParam
|
|||||||
val exit = UInt(OUTPUT, 32)
|
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
|
dutio <> io.jtag
|
||||||
jtag_reset := tbreset
|
|
||||||
|
|
||||||
io.clock := tbclock
|
io.clock := tbclock
|
||||||
io.reset := tbreset
|
io.reset := tbreset
|
||||||
|
|
||||||
io.enable := PlusArg("jtag_rbb_enable", 0, "Enable SimJTAG for JTAG Connections. Simulation will pause until connection is made.")
|
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
|
// Success is determined by the gdbserver
|
||||||
// which is controlling this simulation.
|
// 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,
|
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
|
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.
|
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)
|
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
|
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
|
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
|
Harts are unavailable if they might exist/become available at a later
|
||||||
time. Eg. in a multi-hart system some might temporarily be powered
|
time, or if there are other harts with higher indexes than this one. Eg.
|
||||||
down, or a system might support hot-swapping harts.
|
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
|
def DMI_DMSTATUS = 0x11
|
||||||
|
|
||||||
/* This register controls the overall debug module
|
/* This register controls the overall debug module
|
||||||
as well as the currently selected harts, as defined in \Fhasel.
|
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
|
def DMI_DMCONTROL = 0x10
|
||||||
|
|
||||||
@ -40,15 +57,6 @@ object DMI_RegAddrs {
|
|||||||
*/
|
*/
|
||||||
def DMI_HARTINFO = 0x12
|
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
|
/* This register selects which of the 32-bit portion of the hart array mask register
|
||||||
is accessible in \Rhawindow.
|
is accessible in \Rhawindow.
|
||||||
|
|
||||||
@ -116,11 +124,20 @@ object DMI_RegAddrs {
|
|||||||
|
|
||||||
def DMI_DEVTREEADDR3 = 0x1c
|
def DMI_DEVTREEADDR3 = 0x1c
|
||||||
|
|
||||||
/* Basic read/write registers that may be read or changed by abstract
|
/* If there is more than one DM accessible on this DMI, this register
|
||||||
commands.
|
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
|
/* \Rdatazero through \Rdataeleven are basic read/write registers that may
|
||||||
to be set.
|
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.
|
Attempts to write them while \Fbusy is set does not change their value.
|
||||||
|
|
||||||
@ -133,11 +150,12 @@ object DMI_RegAddrs {
|
|||||||
|
|
||||||
def DMI_DATA11 = 0x0f
|
def DMI_DATA11 = 0x0f
|
||||||
|
|
||||||
/* The {\tt progbuf} registers provide read/write access to the optional
|
/* \Rprogbufzero through \Rprogbuffifteen provide read/write access to the
|
||||||
program buffer.
|
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
|
Accessing these registers while an abstract command is executing causes
|
||||||
to be set.
|
\Fcmderr to be set.
|
||||||
|
|
||||||
Attempts to write them while \Fbusy is set does not change their value.
|
Attempts to write them while \Fbusy is set does not change their value.
|
||||||
*/
|
*/
|
||||||
@ -154,50 +172,114 @@ object DMI_RegAddrs {
|
|||||||
*/
|
*/
|
||||||
def DMI_AUTHDATA = 0x30
|
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
|
def DMI_SBCS = 0x38
|
||||||
|
|
||||||
/* If \Fsbasize is 0, then this register is not present.
|
/* If \Fsbasize is 0, then this register is not present.
|
||||||
|
|
||||||
When the system bus master is busy,
|
When the system bus master is busy, writes to this register will set
|
||||||
writes to this register will set \Fsberror.
|
\Fsbbusyerror and don't do anything else.
|
||||||
|
|
||||||
If \Fsberror is 0 and \Fsbautoread is set then the system bus
|
\begin{steps}{If \Fsberror is 0, \Fsbbusyerror is 0, and \Fsbreadonaddr
|
||||||
master will start
|
is set then writes to this register start the following:}
|
||||||
to read after updating the address from \Faddress. The access size is
|
\item Set \Fsbbusy.
|
||||||
controlled by \Fsbaccess in \Rsbcs.
|
\item Perform a bus read from the new value of {\tt sbaddress}.
|
||||||
|
\item If the read succeeded and \Fsbautoincrement is set, increment
|
||||||
If \Fsbsingleread is set, the bit is cleared.
|
{\tt sbaddress}.
|
||||||
|
\item Clear \Fsbbusy.
|
||||||
|
\end{steps}
|
||||||
*/
|
*/
|
||||||
def DMI_SBADDRESS0 = 0x39
|
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
|
def DMI_SBADDRESS1 = 0x3a
|
||||||
|
|
||||||
/* If \Fsbasize is less than 65, then this register is not present.
|
/* 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
|
def DMI_SBADDRESS2 = 0x3b
|
||||||
|
|
||||||
/* If all of the {\tt sbaccess} bits in \Rsbcs are 0, then this register
|
/* If all of the {\tt sbaccess} bits in \Rsbcs are 0, then this register
|
||||||
is not present.
|
is not present.
|
||||||
|
|
||||||
Any successful system bus read updates the data in this register, and
|
Any successful system bus read updates the data in this register.
|
||||||
marks it no longer stale.
|
|
||||||
|
|
||||||
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:}
|
If the bus master is busy then accesses set \Fsbbusyerror, and don't do
|
||||||
\item If the bus master is busy then accesses set \Fsberror, and
|
anything else.
|
||||||
don't do anything else.
|
|
||||||
\item Start a bus write of {\tt sbdata} to {\tt sbaddress}.
|
\begin{steps}{Writes to this register start the following:}
|
||||||
\item If \Fsbautoincrement is set, increment {\tt sbaddress}.
|
\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}
|
\end{steps}
|
||||||
|
|
||||||
\begin{steps}{Reads from this register:}
|
\begin{steps}{Reads from this register start the following:}
|
||||||
\item If the register is marked stale, then set \Fsberror and don't
|
|
||||||
do anything else.
|
|
||||||
\item ``Return'' the data.
|
\item ``Return'' the data.
|
||||||
\item Mark the register stale.
|
\item Set \Fsbbusy.
|
||||||
\item If \Fsbautoincrement is set, increment {\tt sbaddress}.
|
\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}
|
\end{steps}
|
||||||
|
|
||||||
Only \Rsbdatazero has this behavior. The other {\tt sbdata} registers
|
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
|
/* If \Fsbaccesssixtyfour and \Fsbaccessonetwentyeight are 0, then this
|
||||||
register is not present.
|
register is not present.
|
||||||
|
|
||||||
|
If the bus master is busy then accesses set \Fsbbusyerror, and don't do
|
||||||
|
anything else.
|
||||||
*/
|
*/
|
||||||
def DMI_SBDATA1 = 0x3d
|
def DMI_SBDATA1 = 0x3d
|
||||||
|
|
||||||
/* This register only exists if \Fsbaccessonetwentyeight is 1.
|
/* 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
|
def DMI_SBDATA2 = 0x3e
|
||||||
|
|
||||||
/* This register only exists if \Fsbaccessonetwentyeight is 1.
|
/* 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
|
def DMI_SBDATA3 = 0x3f
|
||||||
|
|
||||||
@ -224,20 +315,7 @@ object DMI_RegAddrs {
|
|||||||
|
|
||||||
class DMSTATUSFields extends Bundle {
|
class DMSTATUSFields extends Bundle {
|
||||||
|
|
||||||
val reserved0 = UInt(5.W)
|
val reserved0 = UInt(9.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)
|
|
||||||
|
|
||||||
/* If 1, then there is an implicit {\tt ebreak} instruction at the
|
/* If 1, then there is an implicit {\tt ebreak} instruction at the
|
||||||
non-existent word immediately after the Program Buffer. This saves
|
non-existent word immediately after the Program Buffer. This saves
|
||||||
@ -248,7 +326,7 @@ class DMSTATUSFields extends Bundle {
|
|||||||
*/
|
*/
|
||||||
val impebreak = Bool()
|
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.
|
/* 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 authbusy = Bool()
|
||||||
|
|
||||||
val reserved3 = UInt(1.W)
|
val reserved2 = UInt(1.W)
|
||||||
|
|
||||||
/* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
|
/* 0: \Rdevtreeaddrzero--\Rdevtreeaddrthree hold information which
|
||||||
is not relevant to the Device Tree.
|
is not relevant to the Device Tree.
|
||||||
@ -401,12 +479,17 @@ class DMCONTROLFields extends Bundle {
|
|||||||
*/
|
*/
|
||||||
val hasel = Bool()
|
val hasel = Bool()
|
||||||
|
|
||||||
/* The DM-specific index of the hart to select. This hart is always part of the
|
/* The low 10 bits of \Fhartsel: the DM-specific index of the hart to
|
||||||
currently selected harts.
|
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
|
/* 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
|
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
|
If \Fdataaccess is 1: Number of 32-bit words in the memory map
|
||||||
dedicated to shadowing the {\tt data} registers.
|
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)
|
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 {
|
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 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
|
/* Number of {\tt data} registers that are implemented as part of the
|
||||||
abstract command interface. Valid sizes are 0 - 12.
|
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 {
|
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.
|
cause the command in \Rcommand to be executed again.
|
||||||
*/
|
*/
|
||||||
val autoexecprogbuf = UInt(16.W)
|
val autoexecprogbuf = UInt(16.W)
|
||||||
|
|
||||||
val reserved0 = UInt(4.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.
|
cause the command in \Rcommand to be executed again.
|
||||||
*/
|
*/
|
||||||
val autoexecdata = UInt(12.W)
|
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 {
|
class DATA0Fields extends Bundle {
|
||||||
|
|
||||||
val data = UInt(32.W)
|
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 {
|
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
|
1: The System Bus interface conforms to this version of the spec.
|
||||||
sbaddress} using the access size set by \Fsbaccess.
|
|
||||||
|
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
|
val reserved0 = UInt(6.W)
|
||||||
writes to the {\tt sbaddress} registers or \Rsbdatazero.
|
|
||||||
|
/* 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
|
0: 8-bit
|
||||||
|
|
||||||
@ -691,8 +776,8 @@ class SBCSFields extends Bundle {
|
|||||||
|
|
||||||
4: 128-bit
|
4: 128-bit
|
||||||
|
|
||||||
If an unsupported system bus access size is written here, the DM
|
If \Fsbaccess has an unsupported value when the DM starts a bus
|
||||||
does not perform the access and sberror is set to 3.
|
access, the access is not performed and \Fsberror is set to 3.
|
||||||
*/
|
*/
|
||||||
val sbaccess = UInt(3.W)
|
val sbaccess = UInt(3.W)
|
||||||
|
|
||||||
@ -704,7 +789,7 @@ class SBCSFields extends Bundle {
|
|||||||
/* When 1, every read from \Rsbdatazero automatically triggers a
|
/* When 1, every read from \Rsbdatazero automatically triggers a
|
||||||
system bus read at the (possibly auto-incremented) address.
|
system bus read at the (possibly auto-incremented) address.
|
||||||
*/
|
*/
|
||||||
val sbautoread = Bool()
|
val sbreadondata = Bool()
|
||||||
|
|
||||||
/* When the debug module's system bus
|
/* When the debug module's system bus
|
||||||
master causes a bus error, this field gets set. The bits in this
|
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.
|
2: A bad address was accessed.
|
||||||
|
|
||||||
3: There was some other error (eg. alignment).
|
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)
|
val sberror = UInt(3.W)
|
||||||
|
|
||||||
|
@ -103,7 +103,7 @@ class TLBusBypassBar(implicit p: Parameters) extends LazyModule
|
|||||||
flight := next_flight
|
flight := next_flight
|
||||||
|
|
||||||
when (next_flight === UInt(0)) { bypass := io.bypass }
|
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
|
out0.a.valid := !stall && in.a.valid && bypass
|
||||||
out1.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(
|
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)))}),
|
0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) =>
|
||||||
timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) =>
|
RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0)))}),
|
||||||
RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"), RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))},
|
timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) => RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"),
|
||||||
timeOffset -> RegFieldGroup("mtime", Some("Timer Register"), RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0)))))
|
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)
|
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 priorityRegDesc(i: Int) = if (i > 0) {
|
||||||
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.")
|
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))
|
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)}))
|
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."),
|
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))}))
|
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) =>
|
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."),
|
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
|
// 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
|
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))
|
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 =>
|
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"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.",
|
s"Writing the interrupt number back completes the interrupt.",
|
||||||
reset = None,
|
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
|
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
|
// AddressSets specify the address space managed by the manager
|
||||||
// Base is the base address, and mask are the bits consumed 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
|
// 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
|
object AddressSet
|
||||||
{
|
{
|
||||||
val everything = AddressSet(0, -1)
|
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. */
|
/** Generate the device tree. */
|
||||||
def bindingTree: ResourceMap = {
|
def bindingTree: ResourceMap = {
|
||||||
eval
|
eval
|
||||||
@ -263,6 +272,9 @@ trait BindingScope
|
|||||||
expand(tokens, Seq(ResourceMap(mapping, Seq(d.label)))) })
|
expand(tokens, Seq(ResourceMap(mapping, Seq(d.label)))) })
|
||||||
ResourceMap(SortedMap("/" -> tree))
|
ResourceMap(SortedMap("/" -> tree))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Collect resource addresses from tree. */
|
||||||
|
def collectResourceAddresses = collect(Nil, bindingTree)
|
||||||
}
|
}
|
||||||
|
|
||||||
object BindingScope
|
object BindingScope
|
||||||
|
@ -22,10 +22,9 @@ abstract class DiplomaticSRAM(
|
|||||||
def mask: List[Boolean] = bigBits(address.mask >> log2Ceil(beatBytes))
|
def mask: List[Boolean] = bigBits(address.mask >> log2Ceil(beatBytes))
|
||||||
|
|
||||||
// Use single-ported memory with byte-write enable
|
// 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)
|
// 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(lanes, Bits(width = bits)))
|
||||||
val mem = SeqMem(size, Vec(beatBytes, Bits(width = 8)))
|
|
||||||
devName.foreach(n => mem.suggestName(n.split("-").last))
|
devName.foreach(n => mem.suggestName(n.split("-").last))
|
||||||
mem
|
mem
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class GroundTestSubsystem(implicit p: Parameters) extends BaseSubsystem
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
tiles.flatMap(_.dcacheOpt).foreach { dc =>
|
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
|
// 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 Chisel._
|
||||||
import chisel3.util.{ReadyValidIO}
|
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.
|
// This information is not used internally by the regmap(...) function.
|
||||||
// However, the author of a RegField may be the best person to provide this
|
// 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 {
|
object RegFieldAccessType extends scala.Enumeration {
|
||||||
type RegFieldAccessType = Value
|
type RegFieldAccessType = Value
|
||||||
val R, W, RW, RSPECIAL, WSPECIAL, RWSPECIAL, OTHER = Value
|
val R, W, RW = Value
|
||||||
}
|
}
|
||||||
import RegFieldAccessType._
|
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 (
|
case class RegFieldDesc (
|
||||||
name: String,
|
name: String,
|
||||||
desc: String,
|
desc: String,
|
||||||
group: Option[String] = None,
|
group: Option[String] = None,
|
||||||
groupDesc: Option[String] = None,
|
groupDesc: Option[String] = None,
|
||||||
access: RegFieldAccessType = RegFieldAccessType.RW,
|
access: RegFieldAccessType = RegFieldAccessType.RW,
|
||||||
|
wrType: Option[RegFieldWrType] = None,
|
||||||
|
rdAction: Option[RegFieldRdAction] = None,
|
||||||
|
volatile: Boolean = false,
|
||||||
|
// TODO: testable?
|
||||||
reset: Option[BigInt] = None,
|
reset: Option[BigInt] = None,
|
||||||
enumerations: Map[BigInt, (String, String)] = Map()
|
enumerations: Map[BigInt, (String, String)] = Map()
|
||||||
){
|
){
|
||||||
}
|
}
|
||||||
|
|
||||||
// Our descriptions are in terms of RegFields only, which is somewhat unusual for
|
object RegFieldDesc {
|
||||||
// developers who are used to things being defined as bitfields within registers.
|
def reserved: RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0))
|
||||||
// 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.
|
// 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 {
|
object RegFieldGroup {
|
||||||
def apply (name: String, desc: Option[String], regs: Seq[RegField], descFirstOnly: Boolean = true): Seq[RegField] = {
|
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])
|
case class RegField(width: Int, read: RegReadFn, write: RegWriteFn, desc: Option[RegFieldDesc])
|
||||||
{
|
{
|
||||||
require (width > 0, s"RegField width must be > 0, not $width")
|
require (width > 0, s"RegField width must be > 0, not $width")
|
||||||
|
|
||||||
def pipelined = !read.combinational || !write.combinational
|
def pipelined = !read.combinational || !write.combinational
|
||||||
|
|
||||||
def readOnly = this.copy(write = (), desc = this.desc.map(_.copy(access = RegFieldAccessType.R)))
|
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
|
object RegField
|
||||||
@ -125,8 +170,7 @@ object RegField
|
|||||||
// Byte address => sequence of bitfields, lowest index => lowest address
|
// Byte address => sequence of bitfields, lowest index => lowest address
|
||||||
type Map = (Int, Seq[RegField])
|
type Map = (Int, Seq[RegField])
|
||||||
|
|
||||||
def apply(n: Int) : RegField = apply(n, (), (),
|
def apply(n: Int) : RegField = apply(n, (), (), Some(RegFieldDesc.reserved))
|
||||||
Some(RegFieldDesc("reserved", "", access = RegFieldAccessType.R, reset = Some(0))))
|
|
||||||
|
|
||||||
def apply(n: Int, r: RegReadFn, w: RegWriteFn) : RegField = apply(n, r, w, None)
|
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))
|
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.
|
// Setting takes priority over clearing.
|
||||||
def w1ToClear(n: Int, reg: UInt, set: UInt, desc: Option[RegFieldDesc] = None): RegField =
|
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) }),
|
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
|
// This RegField wraps an explicit register
|
||||||
// (e.g. Black-Boxed Register) to create a R/W register.
|
// (e.g. Black-Boxed Register) to create a R/W register.
|
||||||
@ -151,26 +195,41 @@ object RegField
|
|||||||
bb.en := valid
|
bb.en := valid
|
||||||
bb.d := data
|
bb.d := data
|
||||||
Bool(true)
|
Bool(true)
|
||||||
}), desc.map{_.copy(access = RegFieldAccessType.RW)})
|
}), desc)
|
||||||
|
|
||||||
// Create byte-sized read-write RegFields out of a large UInt register.
|
// 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
|
// It is updated when any of the (implemented) bytes are written, the non-written
|
||||||
// are all byte-sized, this is also suitable when a register is larger
|
// 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).
|
// than the intended bus width of the device (atomic updates are impossible).
|
||||||
def bytes(reg: UInt, numBytes: Int, desc: Option[RegFieldDesc]): Seq[RegField] = {
|
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 pad = reg | UInt(0, width = 8*numBytes)
|
||||||
val oldBytes = Vec.tabulate(numBytes) { i => pad(8*(i+1)-1, 8*i) }
|
val oldBytes = Vec.tabulate(numBytes) { i => pad(8*(i+1)-1, 8*i) }
|
||||||
val newBytes = Wire(init = oldBytes)
|
val newBytes = Wire(init = oldBytes)
|
||||||
val valids = Wire(init = Vec.fill(numBytes) { Bool(false) })
|
val valids = Wire(init = Vec.fill(numBytes) { Bool(false) })
|
||||||
when (valids.reduce(_ || _)) { reg := newBytes.asUInt }
|
when (valids.reduce(_ || _)) { reg := newBytes.asUInt }
|
||||||
Seq.tabulate(numBytes) { i =>
|
|
||||||
val newDesc = desc.map {d => d.copy(name = d.name + s"_$i")}
|
def wrFn(i: Int): RegWriteFn = RegWriteFn((valid, data) => {
|
||||||
RegField(8, oldBytes(i),
|
|
||||||
RegWriteFn((valid, data) => {
|
|
||||||
valids(i) := valid
|
valids(i) := valid
|
||||||
when (valid) {newBytes(i) := data}
|
when (valid) {newBytes(i) := data}
|
||||||
Bool(true)
|
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] = {
|
def bytes(reg: UInt, desc: Option[RegFieldDesc]): Seq[RegField] = {
|
||||||
val width = reg.getWidth
|
val width = reg.getWidth
|
||||||
|
@ -15,16 +15,20 @@ import freechips.rocketchip.interrupts._
|
|||||||
import freechips.rocketchip.util.property._
|
import freechips.rocketchip.util.property._
|
||||||
|
|
||||||
trait BusErrors extends Bundle {
|
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 {
|
class L1BusErrors(implicit p: Parameters) extends CoreBundle()(p) with BusErrors {
|
||||||
val icache = new ICacheErrors
|
val icache = new ICacheErrors
|
||||||
val dcache = new DCacheErrors
|
val dcache = new DCacheErrors
|
||||||
|
|
||||||
def toErrorList =
|
def toErrorList = List(None, None,
|
||||||
List(None, None, icache.correctable, icache.uncorrectable,
|
icache.correctable.map((_, "I_CORRECTABLE", "Instruction cache or ITIM correctable ECC error ")),
|
||||||
None, Some(dcache.bus), dcache.correctable, dcache.uncorrectable)
|
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)
|
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 interrupt = Bool().asOutput
|
||||||
})
|
})
|
||||||
|
|
||||||
val sources = io.errors.toErrorList
|
val sources_and_desc = io.errors.toErrorList
|
||||||
val cause = Reg(init = UInt(0, log2Ceil(sources.lastIndexWhere(_.nonEmpty) + 1)))
|
val sources = sources_and_desc.map(_.map(_._1))
|
||||||
val value = Reg(UInt(width = sources.flatten.map(_.bits.getWidth).max))
|
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)
|
require(value.getWidth <= regWidth)
|
||||||
|
|
||||||
val enable = Reg(init = Vec(sources.map(_.nonEmpty.B)))
|
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 = 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 = 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 = 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) {
|
for ((((s, en), acc), i) <- (sources zip enable zip accrued).zipWithIndex; if s.nonEmpty) {
|
||||||
when (s.get.valid) {
|
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
|
io.interrupt := (accrued.asUInt & local_interrupt.asUInt).orR
|
||||||
int_out(0) := (accrued.asUInt & global_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(r: UInt, gn: String, d: RegFieldDesc) = RegFieldGroup(gn, None, RegField.bytes(r, (r.getWidth + 7)/8, Some(d)))
|
||||||
def reg(v: Vec[Bool]) = v.map(r => RegField(1, r))
|
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 }
|
def numberRegs(x: Seq[Seq[RegField]]) = x.zipWithIndex.map {case (f, i) => (i * regWidth / 8) -> f }
|
||||||
|
|
||||||
node.regmap(numberRegs(Seq(
|
node.regmap(numberRegs(Seq(
|
||||||
reg(cause),
|
reg(cause, "cause", cause_desc),
|
||||||
reg(value),
|
reg(value, "value", value_desc),
|
||||||
reg(enable),
|
reg(enable, "enable", "Event enable mask", enable_desc),
|
||||||
reg(global_interrupt),
|
reg(global_interrupt, "plic_interrupt", "Platform-level interrupt enable mask", global_interrupt_desc),
|
||||||
reg(accrued),
|
reg(accrued, "accrued", "Accrued event mask" ,accrued_desc),
|
||||||
reg(local_interrupt))):_*)
|
reg(local_interrupt, "local_interrupt", "Hart-local interrupt-enable mask", local_interrupt_desc))):_*)
|
||||||
|
|
||||||
// hardwire mask bits for unsupported sources to 0
|
// hardwire mask bits for unsupported sources to 0
|
||||||
for ((s, i) <- sources.zipWithIndex; if s.isEmpty) {
|
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.mip -> read_mip,
|
||||||
CSRs.mie -> reg_mie,
|
CSRs.mie -> reg_mie,
|
||||||
CSRs.mscratch -> reg_mscratch,
|
CSRs.mscratch -> reg_mscratch,
|
||||||
CSRs.mepc -> reg_mepc.sextTo(xLen),
|
CSRs.mepc -> readEPC(reg_mepc).sextTo(xLen),
|
||||||
CSRs.mbadaddr -> reg_mbadaddr.sextTo(xLen),
|
CSRs.mbadaddr -> reg_mbadaddr.sextTo(xLen),
|
||||||
CSRs.mcause -> reg_mcause,
|
CSRs.mcause -> reg_mcause,
|
||||||
CSRs.mhartid -> io.hartid)
|
CSRs.mhartid -> io.hartid)
|
||||||
|
|
||||||
val debug_csrs = LinkedHashMap[Int,Bits](
|
val debug_csrs = LinkedHashMap[Int,Bits](
|
||||||
CSRs.dcsr -> reg_dcsr.asUInt,
|
CSRs.dcsr -> reg_dcsr.asUInt,
|
||||||
CSRs.dpc -> reg_dpc.sextTo(xLen),
|
CSRs.dpc -> readEPC(reg_dpc).sextTo(xLen),
|
||||||
CSRs.dscratch -> reg_dscratch.asUInt)
|
CSRs.dscratch -> reg_dscratch.asUInt)
|
||||||
|
|
||||||
val fp_csrs = LinkedHashMap[Int,Bits](
|
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.scause -> reg_scause
|
||||||
read_mapping += CSRs.sbadaddr -> reg_sbadaddr.sextTo(xLen)
|
read_mapping += CSRs.sbadaddr -> reg_sbadaddr.sextTo(xLen)
|
||||||
read_mapping += CSRs.sptbr -> reg_sptbr.asUInt
|
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.stvec -> reg_stvec.sextTo(xLen)
|
||||||
read_mapping += CSRs.scounteren -> reg_scounteren
|
read_mapping += CSRs.scounteren -> reg_scounteren
|
||||||
read_mapping += CSRs.mideleg -> reg_mideleg
|
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.spie := true
|
||||||
reg_mstatus.spp := PRV.U
|
reg_mstatus.spp := PRV.U
|
||||||
new_prv := reg_mstatus.spp
|
new_prv := reg_mstatus.spp
|
||||||
io.evec := reg_sepc
|
io.evec := readEPC(reg_sepc)
|
||||||
}.elsewhen (Bool(usingDebug) && io.rw.addr(10)) {
|
}.elsewhen (Bool(usingDebug) && io.rw.addr(10)) {
|
||||||
new_prv := reg_dcsr.prv
|
new_prv := reg_dcsr.prv
|
||||||
reg_debug := false
|
reg_debug := false
|
||||||
io.evec := reg_dpc
|
io.evec := readEPC(reg_dpc)
|
||||||
}.otherwise {
|
}.otherwise {
|
||||||
reg_mstatus.mie := reg_mstatus.mpie
|
reg_mstatus.mie := reg_mstatus.mpie
|
||||||
reg_mstatus.mpie := true
|
reg_mstatus.mpie := true
|
||||||
reg_mstatus.mpp := legalizePrivilege(PRV.U)
|
reg_mstatus.mpp := legalizePrivilege(PRV.U)
|
||||||
new_prv := reg_mstatus.mpp
|
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)) {
|
when (decoded_addr(CSRs.misa)) {
|
||||||
val mask = UInt(isaStringToMask(isaMaskString), xLen)
|
val mask = UInt(isaStringToMask(isaMaskString), xLen)
|
||||||
val f = wdata('f' - 'a')
|
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)
|
if (coreParams.misaWritable)
|
||||||
reg_misa := ~(~wdata | (!f << ('d' - 'a'))) & mask | reg_misa & ~mask
|
reg_misa := ~(~wdata | (!f << ('d' - 'a'))) & mask | reg_misa & ~mask
|
||||||
}
|
}
|
||||||
|
}
|
||||||
when (decoded_addr(CSRs.mip)) {
|
when (decoded_addr(CSRs.mip)) {
|
||||||
// MIP should be modified based on the value in reg_mip, not the value
|
// 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
|
// 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 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)(_|_)
|
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)
|
class DCacheErrors(implicit p: Parameters) extends L1HellaCacheBundle()(p)
|
||||||
with CanHaveErrors {
|
with CanHaveErrors {
|
||||||
val correctable = (cacheParams.tagECC.canCorrect || cacheParams.dataECC.canCorrect).option(Valid(UInt(width = paddrBits)))
|
val correctable = (cacheParams.tagCode.canCorrect || cacheParams.dataCode.canCorrect).option(Valid(UInt(width = paddrBits)))
|
||||||
val uncorrectable = (cacheParams.tagECC.canDetect || cacheParams.dataECC.canDetect).option(Valid(UInt(width = paddrBits)))
|
val uncorrectable = (cacheParams.tagCode.canDetect || cacheParams.dataCode.canDetect).option(Valid(UInt(width = paddrBits)))
|
||||||
val bus = Valid(UInt(width = paddrBits))
|
val bus = Valid(UInt(width = paddrBits))
|
||||||
}
|
}
|
||||||
|
|
||||||
class DCacheDataReq(implicit p: Parameters) extends L1HellaCacheBundle()(p) {
|
class DCacheDataReq(implicit p: Parameters) extends L1HellaCacheBundle()(p) {
|
||||||
val eccBytes = cacheParams.dataECCBytes
|
|
||||||
val addr = Bits(width = untagBits)
|
val addr = Bits(width = untagBits)
|
||||||
val write = Bool()
|
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 wordMask = UInt(width = rowBytes / wordBytes)
|
||||||
val eccMask = UInt(width = wordBytes / eccBytes)
|
val eccMask = UInt(width = wordBytes / eccBytes)
|
||||||
val way_en = Bits(width = nWays)
|
val way_en = Bits(width = nWays)
|
||||||
@ -37,9 +36,6 @@ class DCacheDataArray(implicit p: Parameters) extends L1HellaCacheModule()(p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require(rowBytes % wordBytes == 0)
|
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 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 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))
|
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) {
|
class DCacheModule(outer: DCache) extends HellaCacheModule(outer) {
|
||||||
// no tag ECC support
|
val tECC = cacheParams.tagCode
|
||||||
val tECC = cacheParams.tagECC
|
val dECC = cacheParams.dataCode
|
||||||
val dECC = cacheParams.dataECC
|
|
||||||
val eccBytes = cacheParams.dataECCBytes
|
|
||||||
val eccBits = eccBytes * 8
|
|
||||||
require(isPow2(eccBytes) && eccBytes <= wordBytes)
|
require(isPow2(eccBytes) && eccBytes <= wordBytes)
|
||||||
require(eccBytes == 1 || !dECC.isInstanceOf[IdentityCode])
|
require(eccBytes == 1 || !dECC.isInstanceOf[IdentityCode])
|
||||||
val usingRMW = eccBytes > 1 || usingAtomicsInCache
|
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 }
|
when (s2_correct) { pstore1_storegen_data := s2_data_word_corrected }
|
||||||
|
|
||||||
// flushes
|
// 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 flushed = Reg(init=Bool(true))
|
||||||
val flushing = Reg(init=Bool(false))
|
val flushing = Reg(init=Bool(false))
|
||||||
val flushCounter = Reg(init=UInt(nSets * (nWays-1), log2Ceil(nSets * nWays)))
|
val flushCounter = Reg(init=UInt(nSets * (nWays-1), log2Ceil(nSets * nWays)))
|
||||||
|
@ -19,8 +19,8 @@ case class DCacheParams(
|
|||||||
nWays: Int = 4,
|
nWays: Int = 4,
|
||||||
rowBits: Int = 64,
|
rowBits: Int = 64,
|
||||||
nTLBEntries: Int = 32,
|
nTLBEntries: Int = 32,
|
||||||
tagECC: Code = new IdentityCode,
|
tagECC: Option[String] = None,
|
||||||
dataECC: Code = new IdentityCode,
|
dataECC: Option[String] = None,
|
||||||
dataECCBytes: Int = 1,
|
dataECCBytes: Int = 1,
|
||||||
nMSHRs: Int = 1,
|
nMSHRs: Int = 1,
|
||||||
nSDQ: Int = 17,
|
nSDQ: Int = 17,
|
||||||
@ -31,6 +31,9 @@ case class DCacheParams(
|
|||||||
pipelineWayMux: Boolean = false,
|
pipelineWayMux: Boolean = false,
|
||||||
scratch: Option[BigInt] = None) extends L1CacheParams {
|
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 dataScratchpadBytes: Int = scratch.map(_ => nSets*blockBytes).getOrElse(0)
|
||||||
|
|
||||||
def replacement = new RandomReplacement(nWays)
|
def replacement = new RandomReplacement(nWays)
|
||||||
@ -58,7 +61,11 @@ trait HasL1HellaCacheParameters extends HasL1CacheParameters with HasCoreParamet
|
|||||||
def offsetlsb = wordOffBits
|
def offsetlsb = wordOffBits
|
||||||
def rowWords = rowBits/wordBits
|
def rowWords = rowBits/wordBits
|
||||||
def doNarrowRead = coreDataBits * nWays % rowBits == 0
|
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 encRowBits = encDataBits*rowWords
|
||||||
def lrscCycles = 32 // ISA requires 16-insn LRSC sequences to succeed
|
def lrscCycles = 32 // ISA requires 16-insn LRSC sequences to succeed
|
||||||
def lrscBackoff = 3 // disallow LRSC reacquisition briefly
|
def lrscBackoff = 3 // disallow LRSC reacquisition briefly
|
||||||
|
@ -20,13 +20,15 @@ case class ICacheParams(
|
|||||||
rowBits: Int = 128,
|
rowBits: Int = 128,
|
||||||
nTLBEntries: Int = 32,
|
nTLBEntries: Int = 32,
|
||||||
cacheIdBits: Int = 0,
|
cacheIdBits: Int = 0,
|
||||||
tagECC: Code = new IdentityCode,
|
tagECC: Option[String] = None,
|
||||||
dataECC: Code = new IdentityCode,
|
dataECC: Option[String] = None,
|
||||||
itimAddr: Option[BigInt] = None,
|
itimAddr: Option[BigInt] = None,
|
||||||
prefetch: Boolean = false,
|
prefetch: Boolean = false,
|
||||||
blockBytes: Int = 64,
|
blockBytes: Int = 64,
|
||||||
latency: Int = 2,
|
latency: Int = 2,
|
||||||
fetchBytes: Int = 4) extends L1CacheParams {
|
fetchBytes: Int = 4) extends L1CacheParams {
|
||||||
|
def tagCode: Code = Code.fromString(tagECC)
|
||||||
|
def dataCode: Code = Code.fromString(dataECC)
|
||||||
def replacement = new RandomReplacement(nWays)
|
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)
|
class ICacheErrors(implicit p: Parameters) extends CoreBundle()(p)
|
||||||
with HasL1ICacheParameters
|
with HasL1ICacheParameters
|
||||||
with CanHaveErrors {
|
with CanHaveErrors {
|
||||||
val correctable = (cacheParams.tagECC.canDetect || 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.dataECC.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 {
|
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 :-(
|
// Option.unzip does not exist :-(
|
||||||
val (tl_in, edge_in) = outer.slaveNode.in.headOption.unzip
|
val (tl_in, edge_in) = outer.slaveNode.in.headOption.unzip
|
||||||
|
|
||||||
val tECC = cacheParams.tagECC
|
val tECC = cacheParams.tagCode
|
||||||
val dECC = cacheParams.dataECC
|
val dECC = cacheParams.dataCode
|
||||||
|
|
||||||
require(isPow2(nSets) && isPow2(nWays))
|
require(isPow2(nSets) && isPow2(nWays))
|
||||||
require(!usingVM || pgIdxBits >= untagBits)
|
require(!usingVM || pgIdxBits >= untagBits)
|
||||||
|
@ -668,8 +668,8 @@ class NonBlockingDCacheModule(outer: NonBlockingDCache) extends HellaCacheModule
|
|||||||
require(dataScratchpadSize == 0)
|
require(dataScratchpadSize == 0)
|
||||||
|
|
||||||
// ECC is only supported on the data array
|
// ECC is only supported on the data array
|
||||||
require(cacheParams.tagECC.isInstanceOf[IdentityCode])
|
require(cacheParams.tagCode.isInstanceOf[IdentityCode])
|
||||||
val dECC = cacheParams.dataECC
|
val dECC = cacheParams.dataCode
|
||||||
|
|
||||||
val wb = Module(new WritebackUnit)
|
val wb = Module(new WritebackUnit)
|
||||||
val prober = Module(new ProbeUnit)
|
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.pc := ibuf.io.pc
|
||||||
bpu.io.ea := mem_reg_wdata
|
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_xcpt0 = ibuf.io.inst(0).bits.xcpt0
|
||||||
val id_xcpt1 = ibuf.io.inst(0).bits.xcpt1
|
val id_xcpt1 = ibuf.io.inst(0).bits.xcpt1
|
||||||
val (id_xcpt, id_cause) = checkExceptions(List(
|
val (id_xcpt, id_cause) = checkExceptions(List(
|
||||||
(csr.io.interrupt, csr.io.interrupt_cause),
|
(csr.io.interrupt, csr.io.interrupt_cause),
|
||||||
(bpu.io.debug_if, UInt(CSR.debugTriggerCause)),
|
(bpu.io.debug_if, UInt(CSR.debugTriggerCause)),
|
||||||
(bpu.io.xcpt_if, UInt(Causes.breakpoint)),
|
(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.pf.inst, UInt(Causes.fetch_page_fault)),
|
||||||
(id_xcpt0.ae.inst, UInt(Causes.fetch_access)),
|
(id_xcpt0.ae.inst, UInt(Causes.fetch_access)),
|
||||||
(id_xcpt1.pf.inst, UInt(Causes.fetch_page_fault)),
|
(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)
|
sboard.clear(ll_wen, ll_waddr)
|
||||||
def id_sboard_clear_bypass(r: UInt) = {
|
def id_sboard_clear_bypass(r: UInt) = {
|
||||||
// ll_waddr arrives late when D$ has ECC, so reshuffle the hazard check
|
// 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
|
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))
|
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)] = {
|
abstract class BaseSubsystemModuleImp[+L <: BaseSubsystem](_outer: L) extends BareSubsystemModuleImp(_outer) {
|
||||||
value match {
|
private val mapping: Seq[AddressMapEntry] = {
|
||||||
case r: ResourceAddress => List((path(1), r))
|
outer.collectResourceAddresses.groupBy(_._2).toList.flatMap { case (key, seq) =>
|
||||||
case b: ResourceMapping => List((path(1), ResourceAddress(b.address, b.permissions)))
|
AddressRange.fromSets(key.address).map { r => AddressMapEntry(r, key.permissions, seq.map(_._1)) }
|
||||||
case ResourceMap(value, _) => value.toList.flatMap { case (key, seq) => seq.flatMap(r => collect(key :: path, r)) }
|
}.sortBy(_.range)
|
||||||
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(",")}]}"""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println("Generated Address Map")
|
||||||
|
mapping.map(entry => println(entry.toString((outer.sbus.busView.bundle.addressBits-1)/4 + 1)))
|
||||||
println("")
|
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
|
// 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) })
|
private val allRanges = AddressRange.unify(outer.topManagers.get.flatMap { m => AddressRange.fromSets(m.address) })
|
||||||
|
|
||||||
if (dtsRanges != allRanges) {
|
if (dtsRanges != allRanges) {
|
||||||
|
@ -12,7 +12,7 @@ case class FrontBusParams(
|
|||||||
beatBytes: Int,
|
beatBytes: Int,
|
||||||
blockBytes: Int,
|
blockBytes: Int,
|
||||||
sbusCrossing: SubsystemClockCrossing = SynchronousCrossing(),
|
sbusCrossing: SubsystemClockCrossing = SynchronousCrossing(),
|
||||||
sbusBuffer: BufferParams = BufferParams.default) extends HasTLBusParams
|
sbusBuffer: BufferParams = BufferParams.none) extends HasTLBusParams
|
||||||
|
|
||||||
case object FrontBusKey extends Field[FrontBusParams]
|
case object FrontBusKey extends Field[FrontBusParams]
|
||||||
|
|
||||||
@ -23,21 +23,23 @@ class FrontBus(params: FrontBusParams)
|
|||||||
val crossing = params.sbusCrossing
|
val crossing = params.sbusCrossing
|
||||||
|
|
||||||
def fromPort[D,U,E,B <: Data]
|
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] =
|
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
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) {
|
def fromMasterNode
|
||||||
from("master" named name) { fixFrom(TLFIFOFixer.all, buffers) :=* gen }
|
(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]
|
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] =
|
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
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 = {
|
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)
|
class MemoryBus(params: MemoryBusParams)(implicit p: Parameters) extends TLBusWrapper(params, "memory_bus")(p)
|
||||||
with HasTLXbarPhy {
|
with HasTLXbarPhy {
|
||||||
|
|
||||||
def fromCoherenceManager(
|
def fromCoherenceManager
|
||||||
name: Option[String] = None,
|
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||||
buffer: BufferParams = BufferParams.none)
|
|
||||||
(gen: => TLNode): TLInwardNode = {
|
(gen: => TLNode): TLInwardNode = {
|
||||||
from("coherence_manager" named name) {
|
from("coherence_manager" named name) {
|
||||||
inwardNode := TLBuffer(buffer) := gen
|
inwardNode := TLBuffer(buffer) := gen
|
||||||
|
@ -108,10 +108,10 @@ class PeripheryBus(params: PeripheryBusParams)
|
|||||||
|
|
||||||
|
|
||||||
def toTile
|
def toTile
|
||||||
(name: Option[String] = None, buffers: Int = 0)
|
(name: Option[String] = None, buffer: BufferParams = BufferParams.none)
|
||||||
(gen: => TLNode): TLOutwardNode = {
|
(gen: => TLNode): TLOutwardNode = {
|
||||||
to("tile" named name) { FlipRendering { implicit p =>
|
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))))))
|
id = IdRange(0, 1 << params.idBits))))))
|
||||||
|
|
||||||
private val fifoBits = 1
|
private val fifoBits = 1
|
||||||
sbus.fromPort(Some(portName)) {
|
fbus.fromPort(Some(portName), buffer = BufferParams.default) {
|
||||||
(TLWidthWidget(params.beatBytes)
|
(TLWidthWidget(params.beatBytes)
|
||||||
:= AXI4ToTL()
|
:= AXI4ToTL()
|
||||||
:= AXI4UserYanker(Some(1 << (params.sourceBits - fifoBits - 1)))
|
:= 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)
|
private val master_splitter = LazyModule(new TLSplitter)
|
||||||
inwardNode :=* master_splitter.node
|
inwardNode :=* master_splitter.node
|
||||||
|
|
||||||
protected def fixFromThenSplit(policy: TLFIFOFixer.Policy, buffers: Int): TLInwardNode =
|
protected def fixFromThenSplit(policy: TLFIFOFixer.Policy, buffer: BufferParams): TLInwardNode =
|
||||||
master_splitter.node :=* TLBuffer.chain(buffers).foldLeft(TLFIFOFixer(policy))(_ :=* _)
|
master_splitter.node :=* TLBuffer(buffer) :=* TLFIFOFixer(policy)
|
||||||
|
|
||||||
def busView = master_splitter.node.edges.in.head
|
def busView = master_splitter.node.edges.in.head
|
||||||
|
|
||||||
@ -72,10 +72,10 @@ class SystemBus(params: SystemBusParams)(implicit p: Parameters) extends TLBusWr
|
|||||||
}
|
}
|
||||||
|
|
||||||
def fromTile
|
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 = {
|
(gen: => TLNode): TLInwardNode = {
|
||||||
from("tile" named name) {
|
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]
|
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] =
|
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
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]
|
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] =
|
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
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]
|
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] =
|
(gen: => NodeHandle[D,U,E,B,TLClientPortParameters,TLManagerPortParameters,TLEdgeOut,TLBundle] =
|
||||||
TLIdentity.gen): InwardNodeHandle[D,U,E,B] = {
|
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 busy = Bool(OUTPUT)
|
||||||
val interrupt = Bool(OUTPUT)
|
val interrupt = Bool(OUTPUT)
|
||||||
val exception = Bool(INPUT)
|
val exception = Bool(INPUT)
|
||||||
|
|
||||||
override def cloneType = new RoCCCoreIO()(p).asInstanceOf[this.type]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Base classes for Diplomatic TL2 RoCC units **/
|
/** Base classes for Diplomatic TL2 RoCC units **/
|
||||||
@ -64,7 +62,7 @@ abstract class LazyRoCC(implicit p: Parameters) extends LazyModule {
|
|||||||
val tlNode: TLNode = TLIdentityNode()
|
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
|
// Should be handled differently, eventually
|
||||||
val ptw = Vec(p(RoccNPTWPorts), new TLBPTWIO)
|
val ptw = Vec(p(RoccNPTWPorts), new TLBPTWIO)
|
||||||
val fpu_req = Decoupled(new FPInput)
|
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 =
|
protected def bufferFrom(buffer: BufferParams): TLInwardNode =
|
||||||
inwardNode :=* TLBuffer(buffer)
|
inwardNode :=* TLBuffer(buffer)
|
||||||
|
|
||||||
protected def bufferFrom(buffers: Int): TLInwardNode =
|
protected def fixFrom(policy: TLFIFOFixer.Policy, buffer: BufferParams): TLInwardNode =
|
||||||
TLBuffer.chain(buffers).foldLeft(inwardNode)(_ :=* _)
|
inwardNode :=* TLBuffer(buffer) :=* TLFIFOFixer(policy)
|
||||||
|
|
||||||
protected def fixFrom(policy: TLFIFOFixer.Policy, buffers: Int): TLInwardNode =
|
|
||||||
inwardNode :=* TLBuffer.chain(buffers).foldLeft(TLFIFOFixer(policy))(_ :=* _)
|
|
||||||
|
|
||||||
protected def bufferTo(buffer: BufferParams): TLOutwardNode =
|
protected def bufferTo(buffer: BufferParams): TLOutwardNode =
|
||||||
TLBuffer(buffer) :*= delayNode :*= outwardNode
|
TLBuffer(buffer) :*= delayNode :*= outwardNode
|
||||||
|
|
||||||
protected def bufferTo(buffers: Int): TLOutwardNode =
|
|
||||||
TLBuffer.chain(buffers).foldRight(delayNode)(_ :*= _) :*= outwardNode
|
|
||||||
|
|
||||||
protected def fixedWidthTo(buffer: BufferParams): TLOutwardNode =
|
protected def fixedWidthTo(buffer: BufferParams): TLOutwardNode =
|
||||||
TLWidthWidget(beatBytes) :*= bufferTo(buffer)
|
TLWidthWidget(beatBytes) :*= bufferTo(buffer)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ trait ExampleModule extends HasRegMap
|
|||||||
Some(RegFieldDesc("pending", "Pending: Example of a special (W1ToC) Register. " +
|
Some(RegFieldDesc("pending", "Pending: Example of a special (W1ToC) Register. " +
|
||||||
"Writing a bit here causes it to be reset to 0. " +
|
"Writing a bit here causes it to be reset to 0. " +
|
||||||
"The bits are set when the corresponding bit in 'state' is high.",
|
"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) {
|
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 freechips.rocketchip.util.{HeterogeneousBag, ElaborationArtefacts}
|
||||||
import scala.math.{min,max}
|
import scala.math.{min,max}
|
||||||
|
|
||||||
import org.json4s.JsonDSL._
|
|
||||||
import org.json4s.jackson.JsonMethods.{pretty, render}
|
|
||||||
|
|
||||||
case class TLRegisterNode(
|
case class TLRegisterNode(
|
||||||
address: Seq[AddressSet],
|
address: Seq[AddressSet],
|
||||||
device: Device,
|
device: Device,
|
||||||
@ -85,44 +82,15 @@ case class TLRegisterNode(
|
|||||||
bundleIn.e.ready := Bool(true)
|
bundleIn.e.ready := Bool(true)
|
||||||
|
|
||||||
// Dump out the register map for documentation purposes.
|
// Dump out the register map for documentation purposes.
|
||||||
val regDescs = mapping.flatMap { case (offset, seq) =>
|
val base = address.head.base
|
||||||
var currentBitOffset = 0
|
val baseHex = s"0x${base.toInt.toHexString}"
|
||||||
seq.zipWithIndex.map { case (f, i) => {
|
val name = s"deviceAt${baseHex}" //TODO: It would be better to name this other than "Device at ...."
|
||||||
val tmp = (f.desc.map{ _.name}.getOrElse(s"unnamedRegField${offset.toHexString}_${currentBitOffset}") -> (
|
val json = RegMappingAnnotation.serialize(base, name, mapping:_*)
|
||||||
("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)
|
|
||||||
))
|
|
||||||
|
|
||||||
var suffix = 0
|
var suffix = 0
|
||||||
while( ElaborationArtefacts.contains(s"${base}.${suffix}.regmap.json")){
|
while( ElaborationArtefacts.contains(s"${baseHex}.${suffix}.regmap.json")){
|
||||||
suffix = suffix + 1
|
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,
|
cacheable: Boolean = true,
|
||||||
executable: Boolean = true,
|
executable: Boolean = true,
|
||||||
beatBytes: Int = 4,
|
beatBytes: Int = 4,
|
||||||
|
eccBytes: Int = 1,
|
||||||
devName: Option[String] = None,
|
devName: Option[String] = None,
|
||||||
errors: Seq[AddressSet] = Nil)
|
errors: Seq[AddressSet] = Nil,
|
||||||
|
code: Code = new IdentityCode)
|
||||||
(implicit p: Parameters) extends DiplomaticSRAM(address, beatBytes, devName)
|
(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(
|
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||||
Seq(TLManagerParameters(
|
Seq(TLManagerParameters(
|
||||||
address = List(address) ++ errors,
|
address = List(address) ++ errors,
|
||||||
@ -33,46 +39,127 @@ class TLRAM(
|
|||||||
lazy val module = new LazyModuleImp(this) {
|
lazy val module = new LazyModuleImp(this) {
|
||||||
val (in, edge) = node.in(0)
|
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 addrBits = (mask zip edge.addr_hi(in.a.bits).toBools).filter(_._1).map(_._2)
|
||||||
val a_legal = address.contains(in.a.bits.address)
|
val mem = makeSinglePortedByteWriteSeqMem(1 << addrBits.size, lanes, width)
|
||||||
val memAddress = Cat(addrBits.reverse)
|
|
||||||
val mem = makeSinglePortedByteWriteSeqMem(1 << addrBits.size)
|
|
||||||
|
|
||||||
|
/* 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_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_size = Reg(UInt())
|
||||||
val d_source = Reg(UInt())
|
val d_source = Reg(UInt())
|
||||||
val d_data = Wire(UInt())
|
|
||||||
val d_legal = Reg(Bool())
|
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
|
// Decode raw unregistered SRAM output
|
||||||
when (in.d.fire()) { d_full := Bool(false) }
|
val d_raw_data = Wire(Vec(lanes, Bits(width = width)))
|
||||||
when (in.a.fire()) { d_full := Bool(true) }
|
val d_decoded = d_raw_data.map(lane => code.decode(lane))
|
||||||
in.d.valid := d_full
|
val d_corrected = Cat(d_decoded.map(_.corrected).reverse)
|
||||||
in.a.ready := in.d.ready || !d_full
|
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.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
|
// Formulate a response only when SRAM output is unused or correct
|
||||||
val rdata = Wire(Vec(beatBytes, Bits(width = 8)))
|
val d_pause = d_read && d_ram_valid && d_need_fix
|
||||||
val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) }
|
in.d.valid := d_full && !d_pause
|
||||||
d_data := Cat(rdata.reverse)
|
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()) {
|
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_size := in.a.bits.size
|
||||||
d_source := in.a.bits.source
|
d_source := in.a.bits.source
|
||||||
d_legal := a_legal
|
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
|
// SRAM arbitration
|
||||||
when (in.a.fire() && !read && a_legal) {
|
val a_fire = in.a.fire() && a_legal
|
||||||
mem.write(memAddress, wdata, in.a.bits.mask.toBools)
|
val wen = d_wb || (a_fire && !a_ren)
|
||||||
}
|
// val ren = !d_wb && (a_fire && a_ren)
|
||||||
val ren = in.a.fire() && read
|
val ren = !wen && a_fire // help Chisel infer a RW-port
|
||||||
rdata := mem.readAndHold(memAddress, ren)
|
|
||||||
|
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
|
// Tie off unused channels
|
||||||
in.b.valid := Bool(false)
|
in.b.valid := Bool(false)
|
||||||
@ -88,10 +175,12 @@ object TLRAM
|
|||||||
cacheable: Boolean = true,
|
cacheable: Boolean = true,
|
||||||
executable: Boolean = true,
|
executable: Boolean = true,
|
||||||
beatBytes: Int = 4,
|
beatBytes: Int = 4,
|
||||||
|
eccBytes: Int = 1,
|
||||||
devName: Option[String] = None,
|
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
|
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)
|
val dut = Module(LazyModule(new TLRAMSimple(ramBeatBytes, txns)).module)
|
||||||
io.finished := dut.io.finished
|
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 freechips.rocketchip.amba.axi4._
|
||||||
import scala.math.{min, max}
|
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)(
|
case class TLToAXI4Node(stripBits: Int = 0)(implicit valName: ValName) extends MixedAdapterNode(TLImp, AXI4Imp)(
|
||||||
dFn = { p =>
|
dFn = { p =>
|
||||||
p.clients.foreach { c =>
|
p.clients.foreach { c =>
|
||||||
@ -59,32 +83,25 @@ class TLToAXI4(val combinational: Boolean = true, val adapterName: Option[String
|
|||||||
require (slaves(0).interleavedId.isDefined)
|
require (slaves(0).interleavedId.isDefined)
|
||||||
slaves.foreach { s => require (s.interleavedId == slaves(0).interleavedId) }
|
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
|
// 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 sourceStall = Wire(Vec(edgeIn.client.endSourceId, Bool()))
|
||||||
val sourceTable = Wire(Vec(edgeIn.client.endSourceId, out.aw.bits.id))
|
val sourceTable = Wire(Vec(edgeIn.client.endSourceId, out.aw.bits.id))
|
||||||
val idStall = Wire(init = Vec.fill(edgeOut.master.endId) { Bool(false) })
|
val idStall = Wire(init = Vec.fill(edgeOut.master.endId) { Bool(false) })
|
||||||
var idCount = Array.fill(edgeOut.master.endId) { None:Option[Int] }
|
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) {
|
map.mapping.foreach { case TLToAXI4IdMapEntry(axi4Id, tlId, _, _, fifo) =>
|
||||||
val id = m.id.start + (if (c.requestFifo) 0 else (i >> stripBits))
|
for (i <- 0 until tlId.size) {
|
||||||
sourceStall(c.sourceId.start + i) := idStall(id)
|
val id = axi4Id.start + (if (fifo) 0 else (i >> stripBits))
|
||||||
sourceTable(c.sourceId.start + i) := UInt(id)
|
sourceStall(tlId.start + i) := idStall(id)
|
||||||
}
|
sourceTable(tlId.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)}]}"""
|
|
||||||
}
|
}
|
||||||
|
if (fifo) { idCount(axi4Id.start) = Some(tlId.size) }
|
||||||
}
|
}
|
||||||
|
|
||||||
adapterName.foreach { n =>
|
adapterName.foreach { n =>
|
||||||
println("")
|
println(s"$n AXI4-ID <= TL-Source mapping:\n${map.pretty}\n")
|
||||||
ElaborationArtefacts.add(s"${n}.axi4.json", s"""{"mapping":[${maps.mkString(",")}]}""")
|
ElaborationArtefacts.add(s"$n.axi4.json", s"""{"mapping":[${map.mapping.mkString(",")}]}""")
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to keep the following state from A => D: (size, source)
|
// 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 TLRR1Test( txns= 3*txns, timeout=timeout)),
|
||||||
Module(new TLRAMRationalCrossingTest(txns= 3*txns, timeout=timeout)),
|
Module(new TLRAMRationalCrossingTest(txns= 3*txns, timeout=timeout)),
|
||||||
Module(new TLRAMAsyncCrossingTest( txns= 5*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) => {
|
class WithTLWidthUnitTests extends Config((site, here, up) => {
|
||||||
|
@ -19,7 +19,12 @@ abstract class Code
|
|||||||
def canCorrect: Boolean
|
def canCorrect: Boolean
|
||||||
|
|
||||||
def width(w0: Int): Int
|
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
|
def decode(x: UInt): Decoding
|
||||||
|
|
||||||
/** Copy the bits in x to the right bit positions in an encoded word,
|
/** 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 canCorrect = false
|
||||||
|
|
||||||
def width(w0: Int) = w0
|
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 swizzle(x: UInt) = x
|
||||||
def decode(y: UInt) = new Decoding {
|
def decode(y: UInt) = new Decoding {
|
||||||
def uncorrected = y
|
def uncorrected = y
|
||||||
@ -52,7 +60,7 @@ class ParityCode extends Code
|
|||||||
def canCorrect = false
|
def canCorrect = false
|
||||||
|
|
||||||
def width(w0: Int) = w0+1
|
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 swizzle(x: UInt) = Cat(false.B, x)
|
||||||
def decode(y: UInt) = new Decoding {
|
def decode(y: UInt) = new Decoding {
|
||||||
val uncorrected = y(y.getWidth-2,0)
|
val uncorrected = y(y.getWidth-2,0)
|
||||||
@ -67,20 +75,26 @@ class SECCode extends Code
|
|||||||
def canDetect = true
|
def canDetect = true
|
||||||
def canCorrect = 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) = {
|
def width(k: Int) = {
|
||||||
val m = log2Floor(k) + 1
|
val m = log2Floor(k) + 1
|
||||||
k + m + (if((1 << m) < m+k+1) 1 else 0)
|
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
|
val k = x.getWidth
|
||||||
require(k > 0)
|
require(k > 0)
|
||||||
val n = width(k)
|
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 {
|
val y = for (i <- 1 to n) yield {
|
||||||
if (isPow2(i)) {
|
if (isPow2(i)) {
|
||||||
val r = for (j <- 1 to n; if j != i && (j & i) != 0)
|
val r = for (j <- 1 to n; if j != i && (j & i) != 0)
|
||||||
yield x(mapping(j))
|
yield x(mapping(j))
|
||||||
r reduce (_^_)
|
r.reduce(_^_) ^ poison
|
||||||
} else
|
} else
|
||||||
x(mapping(i))
|
x(mapping(i))
|
||||||
}
|
}
|
||||||
@ -106,7 +120,7 @@ class SECCode extends Code
|
|||||||
val uncorrected = swizzle(y)
|
val uncorrected = swizzle(y)
|
||||||
val corrected = swizzle(((y << 1) ^ UIntToOH(syndrome)) >> 1)
|
val corrected = swizzle(((y << 1) ^ UIntToOH(syndrome)) >> 1)
|
||||||
val correctable = syndrome.orR
|
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)
|
private def mapping(i: Int) = i-1-log2Up(i)
|
||||||
}
|
}
|
||||||
@ -120,7 +134,12 @@ class SECDEDCode extends Code
|
|||||||
private val par = new ParityCode
|
private val par = new ParityCode
|
||||||
|
|
||||||
def width(k: Int) = sec.width(k)+1
|
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 swizzle(x: UInt) = par.swizzle(sec.swizzle(x))
|
||||||
def decode(x: UInt) = new Decoding {
|
def decode(x: UInt) = new Decoding {
|
||||||
val secdec = sec.decode(x(x.getWidth-2,0))
|
val secdec = sec.decode(x(x.getWidth-2,0))
|
||||||
@ -178,3 +197,15 @@ trait CanHaveErrors extends Bundle {
|
|||||||
val correctable: Option[ValidIO[UInt]]
|
val correctable: Option[ValidIO[UInt]]
|
||||||
val uncorrectable: 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.config._
|
||||||
import freechips.rocketchip.diplomacy.LazyModule
|
import freechips.rocketchip.diplomacy.LazyModule
|
||||||
import java.io.{File, FileWriter}
|
import java.io.{File, FileWriter}
|
||||||
import net.jcazevedo.moultingyaml._
|
import firrtl.annotations.JsonProtocol
|
||||||
import firrtl.annotations.AnnotationYamlProtocol._
|
|
||||||
|
|
||||||
/** Representation of the information this Generator needs to collect from external sources. */
|
/** Representation of the information this Generator needs to collect from external sources. */
|
||||||
case class ParsedInputNames(
|
case class ParsedInputNames(
|
||||||
@ -99,9 +98,9 @@ trait GeneratorApp extends App with HasGeneratorUtilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def generateAnno {
|
def generateAnno {
|
||||||
val annotationFile = new File(td, s"$longName.anno")
|
val annotationFile = new File(td, s"$longName.anno.json")
|
||||||
val af = new FileWriter(annotationFile)
|
val af = new FileWriter(annotationFile)
|
||||||
af.write(circuit.annotations.toArray.toYaml.prettyPrint)
|
af.write(JsonProtocol.serialize(circuit.annotations.map(_.toFirrtl)))
|
||||||
af.close()
|
af.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,21 +4,18 @@
|
|||||||
package freechips.rocketchip.util
|
package freechips.rocketchip.util
|
||||||
|
|
||||||
import Chisel._
|
import Chisel._
|
||||||
import chisel3.experimental.{ChiselAnnotation, RawModule}
|
import chisel3.experimental.RawModule
|
||||||
import freechips.rocketchip.config.Parameters
|
import freechips.rocketchip.config.Parameters
|
||||||
import scala.math._
|
import scala.math._
|
||||||
|
|
||||||
class ParameterizedBundle(implicit p: Parameters) extends Bundle
|
class ParameterizedBundle(implicit p: Parameters) extends Bundle
|
||||||
|
|
||||||
// TODO: replace this with an implicit class when @chisel unprotects dontTouchPorts
|
// TODO: replace this with an implicit class when @chisel unprotects dontTouchPorts
|
||||||
trait DontTouch {
|
trait DontTouch { self: RawModule =>
|
||||||
self: RawModule =>
|
|
||||||
|
|
||||||
def dontTouch(data: Data): Unit = data match {
|
def dontTouch(data: Data): Unit = data match {
|
||||||
case agg: Aggregate =>
|
case agg: Aggregate => agg.getElements.foreach(dontTouch)
|
||||||
agg.getElements.foreach(dontTouch)
|
case elt: Element => chisel3.core.dontTouch(elt)
|
||||||
case elt: Element =>
|
|
||||||
annotate(ChiselAnnotation(elt, classOf[firrtl.Transform], "DONTtouch!"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Marks every port as don't touch
|
/** 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 \
|
+define+RANDOMIZE_INVALID_ASSIGN \
|
||||||
+libext+.v \
|
+libext+.v \
|
||||||
|
|
||||||
VCS_OPTS += +vpi
|
|
||||||
VCS_OPTS += -CC "-DVCS_VPI"
|
|
||||||
|
|
||||||
|
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
# Build the simulator
|
# Build the simulator
|
||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
@ -84,5 +80,5 @@ $(simv_debug) : $(sim_vsrcs) $(sim_csrcs)
|
|||||||
#--------------------------------------------------------------------
|
#--------------------------------------------------------------------
|
||||||
|
|
||||||
seed = $(shell date +%s)
|
seed = $(shell date +%s)
|
||||||
exec_simv = $(simv) -q +ntb_random_seed_automatic
|
exec_simv = $(simv) +permissive -q +ntb_random_seed_automatic +permissive-off
|
||||||
exec_simv_debug = $(simv_debug) -q +ntb_random_seed_automatic
|
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)
|
$(generated_dir)/%.v $(generated_dir)/%.conf: $(generated_dir)/%.fir $(FIRRTL_JAR)
|
||||||
mkdir -p $(dir $@)
|
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)
|
$(generated_dir)/$(long_name).behav_srams.v : $(generated_dir)/$(long_name).conf $(mem_gen)
|
||||||
cd $(generated_dir) && \
|
cd $(generated_dir) && \
|
||||||
@ -28,19 +28,19 @@ $(generated_dir)/$(long_name).behav_srams.v : $(generated_dir)/$(long_name).conf
|
|||||||
.PRECIOUS: $(output_dir)/%.vpd
|
.PRECIOUS: $(output_dir)/%.vpd
|
||||||
|
|
||||||
$(output_dir)/%.run: $(output_dir)/% $(simv)
|
$(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)
|
$(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)
|
$(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)
|
$(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)
|
$(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: run-asm-tests run-bmark-tests
|
||||||
run-debug: run-asm-tests-debug run-bmark-tests-debug
|
run-debug: run-asm-tests-debug run-bmark-tests-debug
|
||||||
|
@ -49,6 +49,8 @@ module SimJTAG #(
|
|||||||
bit __jtag_TRSTn;
|
bit __jtag_TRSTn;
|
||||||
int __exit;
|
int __exit;
|
||||||
|
|
||||||
|
reg init_done_sticky;
|
||||||
|
|
||||||
assign #0.1 jtag_TCK = __jtag_TCK;
|
assign #0.1 jtag_TCK = __jtag_TCK;
|
||||||
assign #0.1 jtag_TMS = __jtag_TMS;
|
assign #0.1 jtag_TMS = __jtag_TMS;
|
||||||
assign #0.1 jtag_TDI = __jtag_TDI;
|
assign #0.1 jtag_TDI = __jtag_TDI;
|
||||||
@ -61,8 +63,10 @@ module SimJTAG #(
|
|||||||
if (reset || r_reset) begin
|
if (reset || r_reset) begin
|
||||||
__exit = 0;
|
__exit = 0;
|
||||||
tickCounterReg <= TICK_DELAY;
|
tickCounterReg <= TICK_DELAY;
|
||||||
|
init_done_sticky <= 1'b0;
|
||||||
end else begin
|
end else begin
|
||||||
if (enable && init_done) begin
|
init_done_sticky <= init_done | init_done_sticky;
|
||||||
|
if (enable && init_done_sticky) begin
|
||||||
tickCounterReg <= tickCounterNxt;
|
tickCounterReg <= tickCounterNxt;
|
||||||
if (tickCounterReg == 0) begin
|
if (tickCounterReg == 0) begin
|
||||||
__exit = jtag_tick(
|
__exit = jtag_tick(
|
||||||
@ -72,7 +76,7 @@ module SimJTAG #(
|
|||||||
__jtag_TRSTn,
|
__jtag_TRSTn,
|
||||||
__jtag_TDO);
|
__jtag_TDO);
|
||||||
end
|
end
|
||||||
end // if (enable && init_done)
|
end // if (enable && init_done_sticky)
|
||||||
end // else: !if(reset || r_reset)
|
end // else: !if(reset || r_reset)
|
||||||
end // always @ (posedge clock)
|
end // always @ (posedge clock)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ module TestDriver;
|
|||||||
reg [63:0] max_cycles = 0;
|
reg [63:0] max_cycles = 0;
|
||||||
reg [63:0] dump_start = 0;
|
reg [63:0] dump_start = 0;
|
||||||
reg [63:0] trace_count = 0;
|
reg [63:0] trace_count = 0;
|
||||||
|
reg [1023:0] fsdbfile = 0;
|
||||||
reg [1023:0] vcdplusfile = 0;
|
reg [1023:0] vcdplusfile = 0;
|
||||||
reg [1023:0] vcdfile = 0;
|
reg [1023:0] vcdfile = 0;
|
||||||
int unsigned rand_value;
|
int unsigned rand_value;
|
||||||
@ -50,7 +51,19 @@ module TestDriver;
|
|||||||
`ifdef VCS
|
`ifdef VCS
|
||||||
$vcdplusfile(vcdplusfile);
|
$vcdplusfile(vcdplusfile);
|
||||||
`else
|
`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;
|
$fatal;
|
||||||
`endif
|
`endif
|
||||||
end
|
end
|
||||||
@ -60,7 +73,11 @@ module TestDriver;
|
|||||||
$dumpfile(vcdfile);
|
$dumpfile(vcdfile);
|
||||||
$dumpvars(0, testHarness);
|
$dumpvars(0, testHarness);
|
||||||
end
|
end
|
||||||
`ifdef VCS
|
|
||||||
|
`ifdef FSDB
|
||||||
|
`define VCDPLUSON $fsdbDumpon;
|
||||||
|
`define VCDPLUSCLOSE $fsdbDumpoff;
|
||||||
|
`elsif VCS
|
||||||
`define VCDPLUSON $vcdpluson(0); $vcdplusmemon(0);
|
`define VCDPLUSON $vcdpluson(0); $vcdplusmemon(0);
|
||||||
`define VCDPLUSCLOSE $vcdplusclose; $dumpoff;
|
`define VCDPLUSCLOSE $vcdplusclose; $dumpoff;
|
||||||
`else
|
`else
|
||||||
@ -72,9 +89,9 @@ module TestDriver;
|
|||||||
`define VCDPLUSON
|
`define VCDPLUSON
|
||||||
`define VCDPLUSCLOSE
|
`define VCDPLUSCLOSE
|
||||||
|
|
||||||
if ($test$plusargs("vcdplusfile=") || $test$plusargs("vcdfile="))
|
if ($test$plusargs("vcdplusfile=") || $test$plusargs("vcdfile=") || $test$plusargs("fsdbfile="))
|
||||||
begin
|
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;
|
$fatal;
|
||||||
end
|
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