Refactor package hierarchy and remove legacy bus protocol implementations (#845)
* Refactors package hierarchy. Additionally: - Removes legacy ground tests and configs - Removes legacy bus protocol implementations - Removes NTiles - Adds devices package - Adds more functions to util package
This commit is contained in:
1147
src/main/scala/devices/debug/Debug.scala
Normal file
1147
src/main/scala/devices/debug/Debug.scala
Normal file
File diff suppressed because it is too large
Load Diff
19
src/main/scala/devices/debug/DebugRomContents.scala
Normal file
19
src/main/scala/devices/debug/DebugRomContents.scala
Normal file
@ -0,0 +1,19 @@
|
||||
// This file was auto-generated by 'make publish' in debug/ directory.
|
||||
|
||||
package freechips.rocketchip.devices.debug
|
||||
|
||||
object DebugRomContents {
|
||||
|
||||
def apply() : Array[Byte] = { Array (
|
||||
0x6f, 0x00, 0xc0, 0x00, 0x6f, 0x00, 0xc0, 0x04, 0x6f, 0x00, 0x40, 0x03,
|
||||
0x0f, 0x00, 0xf0, 0x0f, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x40, 0xf1,
|
||||
0x23, 0x20, 0x80, 0x10, 0x03, 0x44, 0x04, 0x40, 0x13, 0x74, 0x14, 0x00,
|
||||
0x63, 0x10, 0x04, 0x02, 0x73, 0x24, 0x40, 0xf1, 0x03, 0x44, 0x04, 0x40,
|
||||
0x13, 0x74, 0x24, 0x00, 0xe3, 0x18, 0x04, 0xfc, 0x6f, 0xf0, 0xdf, 0xfd,
|
||||
0x23, 0x26, 0x00, 0x10, 0x73, 0x00, 0x10, 0x00, 0x73, 0x24, 0x20, 0x7b,
|
||||
0x23, 0x22, 0x00, 0x10, 0x67, 0x00, 0x00, 0x30, 0x73, 0x24, 0x40, 0xf1,
|
||||
0x23, 0x24, 0x80, 0x10, 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b
|
||||
).map(_.toByte) }
|
||||
|
||||
}
|
||||
|
68
src/main/scala/devices/debug/abstract_commands.scala
Normal file
68
src/main/scala/devices/debug/abstract_commands.scala
Normal file
@ -0,0 +1,68 @@
|
||||
package freechips.rocketchip.devices.debug
|
||||
|
||||
import Chisel._
|
||||
|
||||
// This file was auto-generated from the repository at https://github.com/sifive/riscv-debug-spec.git,
|
||||
// 'make chisel'
|
||||
|
||||
object AC_RegAddrs {
|
||||
}
|
||||
|
||||
class ACCESS_REGISTERFields extends Bundle {
|
||||
|
||||
/* This is 0 to indicate Access Register Command.
|
||||
*/
|
||||
val cmdtype = UInt(8.W)
|
||||
|
||||
val reserved0 = UInt(1.W)
|
||||
|
||||
/* 2: Access the lowest 32 bits of the register.
|
||||
|
||||
3: Access the lowest 64 bits of the register.
|
||||
|
||||
4: Access the lowest 128 bits of the register.
|
||||
|
||||
If \Fsize specifies a size larger than the register's actual size,
|
||||
then the access must fail. If a register is accessible, then reads of \Fsize
|
||||
less than or equal to the register's actual size must be supported.
|
||||
*/
|
||||
val size = UInt(3.W)
|
||||
|
||||
val reserved1 = UInt(1.W)
|
||||
|
||||
/* When 1, execute the program in the Program Buffer exactly once
|
||||
after performing the transfer, if any.
|
||||
*/
|
||||
val postexec = Bool()
|
||||
|
||||
/* 0: Don't do the operation specified by \Fwrite.
|
||||
|
||||
1: Do the operation specified by \Fwrite.
|
||||
*/
|
||||
val transfer = Bool()
|
||||
|
||||
/* When \Ftransfer is set:
|
||||
0: Copy data from the specified register into {\tt arg0} portion
|
||||
of {\tt data}.
|
||||
|
||||
1: Copy data from {\tt arg0} portion of {\tt data} into the
|
||||
specified register.
|
||||
*/
|
||||
val write = Bool()
|
||||
|
||||
/* Number of the register to access, as described in Table~\ref{tab:regno}.
|
||||
*/
|
||||
val regno = UInt(16.W)
|
||||
|
||||
}
|
||||
|
||||
class QUICK_ACCESSFields extends Bundle {
|
||||
|
||||
/* This is 1 to indicate Quick Access command.
|
||||
*/
|
||||
val cmdtype = UInt(8.W)
|
||||
|
||||
val reserved0 = UInt(24.W)
|
||||
|
||||
}
|
||||
|
859
src/main/scala/devices/debug/dm_registers.scala
Normal file
859
src/main/scala/devices/debug/dm_registers.scala
Normal file
@ -0,0 +1,859 @@
|
||||
package freechips.rocketchip.devices.debug
|
||||
|
||||
import Chisel._
|
||||
|
||||
// This file was auto-generated from the repository at https://github.com/sifive/riscv-debug-spec.git,
|
||||
// 'make chisel'
|
||||
|
||||
object DMI_RegAddrs {
|
||||
/* The address of this register will not change in the future, because it
|
||||
contains \Fversion. It has changed from version 0.11 of this spec.
|
||||
|
||||
This register reports status for the overall debug module
|
||||
as well as the currently selected harts, as defined in \Fhasel.
|
||||
*/
|
||||
def DMI_DMSTATUS = 0x11
|
||||
|
||||
/* This register controls the overall debug module
|
||||
as well as the currently selected harts, as defined in \Fhasel.
|
||||
*/
|
||||
def DMI_DMCONTROL = 0x10
|
||||
|
||||
/* This register gives information about the hart currently
|
||||
selected by \Fhartsel.
|
||||
|
||||
This register is optional. If it is not present it should
|
||||
read all-zero.
|
||||
|
||||
If this register is included, the debugger can do more with
|
||||
the Program Buffer by writing programs which
|
||||
explicitly access the {\tt data} and/or {\tt dscratch}
|
||||
registers.
|
||||
*/
|
||||
def DMI_HARTINFO = 0x12
|
||||
|
||||
/* This register contains a summary of which harts are halted.
|
||||
|
||||
Each bit contains the logical OR of 32 halt bits. When there are a
|
||||
large number of harts in the system, the debugger can first read this
|
||||
register, and then read from the halt region (0x40--0x5f) to determine
|
||||
which hart is the one that is halted.
|
||||
*/
|
||||
def DMI_HALTSUM = 0x13
|
||||
|
||||
/* This register selects which of the 32-bit portion of the hart array mask register
|
||||
is accessible in \Rhawindow.
|
||||
|
||||
The hart array mask register provides a mask of all harts controlled by
|
||||
the debug module. A hart is part of the currently selected harts if
|
||||
the corresponding bit is set in the hart array mask register and
|
||||
\Fhasel in \Rdmcontrol is 1, or if the hart is selected by \Fhartsel.
|
||||
*/
|
||||
def DMI_HAWINDOWSEL = 0x14
|
||||
|
||||
/* This register provides R/W access to a 32-bit portion of the
|
||||
hart array mask register. The position of the window is determined by
|
||||
\Rhawindowsel.
|
||||
*/
|
||||
def DMI_HAWINDOW = 0x15
|
||||
|
||||
def DMI_ABSTRACTCS = 0x16
|
||||
|
||||
/* Writes to this register cause the corresponding abstract command to be
|
||||
executed.
|
||||
|
||||
Writing while an abstract command is executing causes \Fcmderr to be set.
|
||||
|
||||
If \Fcmderr is non-zero, writes to this register are ignored.
|
||||
|
||||
\begin{commentary}
|
||||
\Fcmderr inhibits starting a new command to accommodate debuggers
|
||||
that, for performance reasons, send several commands to be executed
|
||||
in a row without checking \Fcmderr in between. They can safely do
|
||||
so and check \Fcmderr at the end without worrying that one command
|
||||
failed but then a later command (which might have depended on the
|
||||
previous one succeeding) passed.
|
||||
\end{commentary}
|
||||
*/
|
||||
def DMI_COMMAND = 0x17
|
||||
|
||||
/* This register is optional. Including it allows more efficient burst accesses.
|
||||
Debugger can attempt to set bits and read them back to determine if the functionality is supported.
|
||||
*/
|
||||
def DMI_ABSTRACTAUTO = 0x18
|
||||
|
||||
/* The Configuration String is described in the RISC-V Priviledged Specification.
|
||||
When {\tt cfgstrvalid} is set, reading this register returns bits 31:0 of the configuration
|
||||
string address. Reading the other {\tt cfgstraddr} registers returns the upper bits of the
|
||||
address.
|
||||
|
||||
When system bus mastering is implemented, this should be the
|
||||
address that should be used with the System Bus Access module. Otherwise,
|
||||
this should be the address that should be used to access the
|
||||
config string when \Fhartsel=0.
|
||||
|
||||
If {\tt cfgstrvalid} is 0, then the {\tt cfgstraddr} registers
|
||||
hold identifier information which is not
|
||||
further specified in this document.
|
||||
*/
|
||||
def DMI_CFGSTRADDR0 = 0x19
|
||||
|
||||
def DMI_CFGSTRADDR1 = 0x1a
|
||||
|
||||
def DMI_CFGSTRADDR2 = 0x1b
|
||||
|
||||
def DMI_CFGSTRADDR3 = 0x1c
|
||||
|
||||
/* Basic read/write registers that may be read or changed by abstract
|
||||
commands.
|
||||
|
||||
Accessing them while an abstract command is executing causes \Fcmderr
|
||||
to be set.
|
||||
|
||||
The values in these registers may not be preserved after an abstract
|
||||
command is executed. The only guarantees on their contents are the ones
|
||||
offered by the command in question. If the command fails, no
|
||||
assumptions can be made about the contents of these registers.
|
||||
*/
|
||||
def DMI_DATA0 = 0x04
|
||||
|
||||
def DMI_DATA11 = 0x0f
|
||||
|
||||
/* The {\tt progbuf} registers provide read/write access to the optional
|
||||
program buffer.
|
||||
*/
|
||||
def DMI_PROGBUF0 = 0x20
|
||||
|
||||
def DMI_PROGBUF15 = 0x2f
|
||||
|
||||
/* This register serves as a 32-bit serial port to the authentication
|
||||
module.
|
||||
|
||||
When \Fauthbusy is clear, the debugger can communicate with the
|
||||
authentication module by reading or writing this register. There is no
|
||||
separate mechanism to signal overflow/underflow.
|
||||
*/
|
||||
def DMI_AUTHDATA = 0x30
|
||||
|
||||
/* If \Fserialcount is 0, this register is not present.
|
||||
*/
|
||||
def DMI_SERCS = 0x34
|
||||
|
||||
/* If \Fserialcount is 0, this register is not present.
|
||||
|
||||
This register provides access to the write data queue of the serial port
|
||||
selected by \Fserial in \Rsercs.
|
||||
|
||||
If the {\tt error} bit is not set and the queue is not full, a write to this register
|
||||
adds the written data to the core-to-debugger queue.
|
||||
Otherwise the {\tt error} bit is set and the write returns error.
|
||||
|
||||
A read to this register returns the last data written.
|
||||
*/
|
||||
def DMI_SERTX = 0x35
|
||||
|
||||
/* If \Fserialcount is 0, this register is not present.
|
||||
|
||||
This register provides access to the read data queues of the serial port
|
||||
selected by \Fserial in \Rsercs.
|
||||
|
||||
If the {\tt error} bit is not set and the queue is not empty, a read from this register reads the
|
||||
oldest entry in the debugger-to-core queue, and removes that entry from the queue.
|
||||
Otherwise the {\tt error} bit is set and the read returns error.
|
||||
*/
|
||||
def DMI_SERRX = 0x36
|
||||
|
||||
def DMI_SBCS = 0x38
|
||||
|
||||
/* If \Fsbasize is 0, then this register is not present.
|
||||
|
||||
When the system bus master is busy,
|
||||
writes to this register will return error
|
||||
and \Fsberror is set.
|
||||
|
||||
If \Fsberror is 0 and \Fsbautoread is set then the system bus
|
||||
master will start
|
||||
to read after updating the address from \Faddress. The access size is
|
||||
controlled by \Fsbaccess in \Rsbcs.
|
||||
|
||||
If \Fsbsingleread is set, the bit is cleared.
|
||||
*/
|
||||
def DMI_SBADDRESS0 = 0x39
|
||||
|
||||
def DMI_SBADDRESS1 = 0x3a
|
||||
|
||||
/* If \Fsbasize is less than 65, then this register is not present.
|
||||
*/
|
||||
def DMI_SBADDRESS2 = 0x3b
|
||||
|
||||
/* If all of the {\tt sbaccess} bits in \Rsbcs are 0, then this register
|
||||
is not present.
|
||||
|
||||
If \Fsberror isn't 0 then accesses return error, and don't do anything
|
||||
else.
|
||||
|
||||
Writes to this register:
|
||||
|
||||
1. If the bus master is busy then accesses set \Fsberror, return error,
|
||||
and don't do anything else.
|
||||
|
||||
2. Update internal data.
|
||||
|
||||
3. Start a bus write of the internal data to the internal address.
|
||||
|
||||
4. If \Fsbautoincrement is set, increment the internal address.
|
||||
|
||||
Reads to this register:
|
||||
|
||||
1. If bits 31:0 of the internal data register haven't been updated
|
||||
since the last time this register was read, then set \Fsberror, return
|
||||
error, and don't do anything else.
|
||||
|
||||
2. ``Return'' the data.
|
||||
|
||||
3. If \Fsbautoincrement is set, increment the internal address.
|
||||
|
||||
4. If \Fsbautoread is set, start another system bus read.
|
||||
*/
|
||||
def DMI_SBDATA0 = 0x3c
|
||||
|
||||
/* If \Fsbaccesssixtyfour and \Fsbaccessonetwentyeight are 0, then this
|
||||
register is not present.
|
||||
*/
|
||||
def DMI_SBDATA1 = 0x3d
|
||||
|
||||
/* This register only exists if \Fsbaccessonetwentyeight is 1.
|
||||
*/
|
||||
def DMI_SBDATA2 = 0x3e
|
||||
|
||||
/* This register only exists if \Fsbaccessonetwentyeight is 1.
|
||||
*/
|
||||
def DMI_SBDATA3 = 0x3f
|
||||
|
||||
}
|
||||
|
||||
class DMSTATUSFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(14.W)
|
||||
|
||||
/* This field is 1 when all currently selected harts have acknowledged the previous \Fresumereq.
|
||||
*/
|
||||
val allresumeack = Bool()
|
||||
|
||||
/* This field is 1 when any currently selected hart has acknowledged the previous \Fresumereq.
|
||||
*/
|
||||
val anyresumeack = Bool()
|
||||
|
||||
/* This field is 1 when all currently selected harts do not exist in this system.
|
||||
*/
|
||||
val allnonexistent = Bool()
|
||||
|
||||
/* This field is 1 when any currently selected hart does not exist in this system.
|
||||
*/
|
||||
val anynonexistent = Bool()
|
||||
|
||||
/* This field is 1 when all currently selected harts are unavailable.
|
||||
*/
|
||||
val allunavail = Bool()
|
||||
|
||||
/* This field is 1 when any currently selected hart is unavailable.
|
||||
*/
|
||||
val anyunavail = Bool()
|
||||
|
||||
/* This field is 1 when all currently selected harts are running.
|
||||
*/
|
||||
val allrunning = Bool()
|
||||
|
||||
/* This field is 1 when any currently selected hart is running.
|
||||
*/
|
||||
val anyrunning = Bool()
|
||||
|
||||
/* This field is 1 when all currently selected harts are halted.
|
||||
*/
|
||||
val allhalted = Bool()
|
||||
|
||||
/* This field is 1 when any currently selected hart is halted.
|
||||
*/
|
||||
val anyhalted = Bool()
|
||||
|
||||
/* 0 when authentication is required before using the DM. 1 when the
|
||||
authentication check has passed. On components that don't implement
|
||||
authentication, this bit must be preset as 1.
|
||||
*/
|
||||
val authenticated = Bool()
|
||||
|
||||
/* 0: The authentication module is ready to process the next
|
||||
read/write to \Rauthdata.
|
||||
|
||||
1: The authentication module is busy. Accessing \Rauthdata results
|
||||
in unspecified behavior.
|
||||
|
||||
\Fauthbusy only becomes set in immediate response to an access to
|
||||
\Rauthdata.
|
||||
*/
|
||||
val authbusy = Bool()
|
||||
|
||||
val reserved1 = UInt(1.W)
|
||||
|
||||
val cfgstrvalid = Bool()
|
||||
|
||||
/* Reserved for future use. Reads as 0.
|
||||
*/
|
||||
val versionhi = UInt(2.W)
|
||||
|
||||
/* 00: There is no Debug Module present.
|
||||
|
||||
01: There is a Debug Module and it conforms to version 0.11 of this
|
||||
specification.
|
||||
|
||||
10: There is a Debug Module and it conforms to version 0.13 of this
|
||||
specification.
|
||||
|
||||
11: Reserved for future use.
|
||||
*/
|
||||
val versionlo = UInt(2.W)
|
||||
|
||||
}
|
||||
|
||||
class DMCONTROLFields extends Bundle {
|
||||
|
||||
/* Halt request signal for all currently selected harts. When set to 1, the
|
||||
hart will halt if it is not currently halted.
|
||||
Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
|
||||
|
||||
Writes apply to the new value of \Fhartsel and \Fhasel.
|
||||
*/
|
||||
val haltreq = Bool()
|
||||
|
||||
/* Resume request signal for all currently selected harts. When set to 1,
|
||||
the hart will resume if it is currently halted.
|
||||
Setting both \Fhaltreq and \Fresumereq leads to undefined behavior.
|
||||
|
||||
Writes apply to the new value of \Fhartsel and \Fhasel.
|
||||
*/
|
||||
val resumereq = Bool()
|
||||
|
||||
/* This optional bit controls reset to all the currently selected harts.
|
||||
To perform a reset the debugger writes 1, and then writes 0 to
|
||||
deassert the reset signal.
|
||||
|
||||
If this feature is not implemented, the bit always stays 0, so
|
||||
after writing 1 the debugger can read the register back to see if
|
||||
the feature is supported.
|
||||
|
||||
Writes apply to the new value of \Fhartsel and \Fhasel.
|
||||
*/
|
||||
val hartreset = Bool()
|
||||
|
||||
val reserved0 = UInt(2.W)
|
||||
|
||||
/* Selects the definition of currently selected harts.
|
||||
|
||||
0: There is a single currently selected hart, that selected by \Fhartsel.
|
||||
|
||||
1: There may be multiple currently selected harts -- that selected by \Fhartsel,
|
||||
plus those selected by the hart array mask register.
|
||||
|
||||
An implementation which does not implement the hart array mask register
|
||||
should tie this field to 0. A debugger which wishes to use the hart array
|
||||
mask register feature should set this bit and read back to see if the functionality
|
||||
is supported.
|
||||
*/
|
||||
val hasel = Bool()
|
||||
|
||||
/* The DM-specific index of the hart to select. This hart is always part of the
|
||||
currently selected harts.
|
||||
*/
|
||||
val hartsel = UInt(10.W)
|
||||
|
||||
val reserved1 = UInt(14.W)
|
||||
|
||||
/* This bit controls the reset signal from the DM to the rest of the
|
||||
system. To perform a reset the debugger writes 1, and then writes 0
|
||||
to deassert the reset.
|
||||
*/
|
||||
val ndmreset = Bool()
|
||||
|
||||
/* This bit serves as a reset signal for the Debug Module itself.
|
||||
|
||||
0: The module's state, including authentication mechanism,
|
||||
takes its reset values (the \Fdmactive bit is the only bit which can
|
||||
be written to something other than its reset value).
|
||||
|
||||
1: The module functions normally.
|
||||
|
||||
No other mechanism should exist that may result in resetting the
|
||||
Debug Module after power up, including the platform's system reset
|
||||
or Debug Transport reset signals.
|
||||
|
||||
A debugger should pulse this bit low to ensure that the Debug
|
||||
Module is fully reset and ready to use.
|
||||
|
||||
Implementations may use this bit to aid debugging, for example by
|
||||
preventing the Debug Module from being power gated while debugging
|
||||
is active.
|
||||
*/
|
||||
val dmactive = Bool()
|
||||
|
||||
}
|
||||
|
||||
class HARTINFOFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(8.W)
|
||||
|
||||
/* Number of {\tt dscratch} registers available for the debugger
|
||||
to use during program buffer execution, starting from \Rdscratchzero.
|
||||
The debugger can make no assumptions about the contents of these
|
||||
registers between commands.
|
||||
*/
|
||||
val nscratch = UInt(4.W)
|
||||
|
||||
val reserved1 = UInt(3.W)
|
||||
|
||||
/* 0: The {\tt data} registers are shadowed in the hart by CSR
|
||||
registers. Each CSR register is XLEN bits in size, and corresponds
|
||||
to a single argument, per Table~\ref{tab:datareg}.
|
||||
|
||||
1: The {\tt data} registers are shadowed in the hart's memory map.
|
||||
Each register takes up 4 bytes in the memory map.
|
||||
*/
|
||||
val dataaccess = Bool()
|
||||
|
||||
/* If \Fdataaccess is 0: Number of CSR registers dedicated to
|
||||
shadowing the {\tt data} registers.
|
||||
|
||||
If \Fdataaccess is 1: Number of 32-bit words in the memory map
|
||||
dedicated to shadowing the {\tt data} registers.
|
||||
*/
|
||||
val datasize = UInt(4.W)
|
||||
|
||||
/* If \Fdataaccess is 0: The number of the first CSR dedicated to
|
||||
shadowing the {\tt data} registers.
|
||||
|
||||
If \Fdataaccess is 1: Signed address of RAM where the {\tt data}
|
||||
registers are shadowed.
|
||||
*/
|
||||
val dataaddr = UInt(12.W)
|
||||
|
||||
}
|
||||
|
||||
class HALTSUMFields extends Bundle {
|
||||
|
||||
val halt1023_992 = Bool()
|
||||
|
||||
val halt991_960 = Bool()
|
||||
|
||||
val halt959_928 = Bool()
|
||||
|
||||
val halt927_896 = Bool()
|
||||
|
||||
val halt895_864 = Bool()
|
||||
|
||||
val halt863_832 = Bool()
|
||||
|
||||
val halt831_800 = Bool()
|
||||
|
||||
val halt799_768 = Bool()
|
||||
|
||||
val halt767_736 = Bool()
|
||||
|
||||
val halt735_704 = Bool()
|
||||
|
||||
val halt703_672 = Bool()
|
||||
|
||||
val halt671_640 = Bool()
|
||||
|
||||
val halt639_608 = Bool()
|
||||
|
||||
val halt607_576 = Bool()
|
||||
|
||||
val halt575_544 = Bool()
|
||||
|
||||
val halt543_512 = Bool()
|
||||
|
||||
val halt511_480 = Bool()
|
||||
|
||||
val halt479_448 = Bool()
|
||||
|
||||
val halt447_416 = Bool()
|
||||
|
||||
val halt415_384 = Bool()
|
||||
|
||||
val halt383_352 = Bool()
|
||||
|
||||
val halt351_320 = Bool()
|
||||
|
||||
val halt319_288 = Bool()
|
||||
|
||||
val halt287_256 = Bool()
|
||||
|
||||
val halt255_224 = Bool()
|
||||
|
||||
val halt223_192 = Bool()
|
||||
|
||||
val halt191_160 = Bool()
|
||||
|
||||
val halt159_128 = Bool()
|
||||
|
||||
val halt127_96 = Bool()
|
||||
|
||||
val halt95_64 = Bool()
|
||||
|
||||
val halt63_32 = Bool()
|
||||
|
||||
val halt31_0 = Bool()
|
||||
|
||||
}
|
||||
|
||||
class HAWINDOWSELFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(27.W)
|
||||
|
||||
val hawindowsel = UInt(5.W)
|
||||
|
||||
}
|
||||
|
||||
class HAWINDOWFields extends Bundle {
|
||||
|
||||
val maskdata = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class ABSTRACTCSFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(3.W)
|
||||
|
||||
/* Size of the Program Buffer, in 32-bit words. Valid sizes are 0 - 16.
|
||||
|
||||
TODO: Explain what can be done with each size of the buffer, to suggest
|
||||
why you would want more or less words.
|
||||
*/
|
||||
val progsize = UInt(5.W)
|
||||
|
||||
val reserved1 = UInt(11.W)
|
||||
|
||||
/* 1: An abstract command is currently being executed.
|
||||
|
||||
This bit is set as soon as \Rcommand is written, and is
|
||||
not cleared until that command has completed.
|
||||
*/
|
||||
val busy = Bool()
|
||||
|
||||
val reserved2 = UInt(1.W)
|
||||
|
||||
/* Gets set if an abstract command fails. The bits in this field remain set until
|
||||
they are cleared by writing 1 to them. No abstract command is
|
||||
started until the value is reset to 0.
|
||||
|
||||
0 (none): No error.
|
||||
|
||||
1 (busy): An abstract command was executing while \Rcommand or one
|
||||
of the {\tt data} registers was accessed.
|
||||
|
||||
2 (not supported): The requested command is not supported. A
|
||||
command that is not supported while the hart is running may be
|
||||
supported when it is halted.
|
||||
|
||||
3 (exception): An exception occurred while executing the command
|
||||
(eg. while executing the Program Buffer).
|
||||
|
||||
4 (halt/resume): An abstract command couldn't execute because the
|
||||
hart wasn't in the expected state (running/halted).
|
||||
|
||||
7 (other): The command failed for another reason.
|
||||
*/
|
||||
val cmderr = UInt(3.W)
|
||||
|
||||
val reserved3 = UInt(3.W)
|
||||
|
||||
/* Number of {\tt data} registers that are implemented as part of the
|
||||
abstract command interface. Valid sizes are 0 - 8.
|
||||
*/
|
||||
val datacount = UInt(5.W)
|
||||
|
||||
}
|
||||
|
||||
class COMMANDFields extends Bundle {
|
||||
|
||||
/* The type determines the overall functionality of this
|
||||
abstract command.
|
||||
*/
|
||||
val cmdtype = UInt(8.W)
|
||||
|
||||
/* This field is interpreted in a command-specific manner,
|
||||
described for each abstract command.
|
||||
*/
|
||||
val control = UInt(24.W)
|
||||
|
||||
}
|
||||
|
||||
class ABSTRACTAUTOFields extends Bundle {
|
||||
|
||||
/* When a bit in this field is 1, read or write accesses the corresponding {\tt progbuf} word
|
||||
cause the command in \Rcommand to be executed again.
|
||||
*/
|
||||
val autoexecprogbuf = UInt(16.W)
|
||||
|
||||
val reserved0 = UInt(4.W)
|
||||
|
||||
/* When a bit in this field is 1, read or write accesses the corresponding {\tt data} word
|
||||
cause the command in \Rcommand to be executed again.
|
||||
*/
|
||||
val autoexecdata = UInt(12.W)
|
||||
|
||||
}
|
||||
|
||||
class CFGSTRADDR0Fields extends Bundle {
|
||||
|
||||
val addr = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class DATA0Fields extends Bundle {
|
||||
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class PROGBUF0Fields extends Bundle {
|
||||
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class AUTHDATAFields extends Bundle {
|
||||
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SERCSFields extends Bundle {
|
||||
|
||||
/* Number of supported serial ports.
|
||||
*/
|
||||
val serialcount = UInt(4.W)
|
||||
|
||||
val reserved0 = UInt(1.W)
|
||||
|
||||
/* Select which serial port is accessed by \Rserrx and \Rsertx.
|
||||
*/
|
||||
val serial = UInt(3.W)
|
||||
|
||||
val error7 = Bool()
|
||||
|
||||
val valid7 = Bool()
|
||||
|
||||
val full7 = Bool()
|
||||
|
||||
val error6 = Bool()
|
||||
|
||||
val valid6 = Bool()
|
||||
|
||||
val full6 = Bool()
|
||||
|
||||
val error5 = Bool()
|
||||
|
||||
val valid5 = Bool()
|
||||
|
||||
val full5 = Bool()
|
||||
|
||||
val error4 = Bool()
|
||||
|
||||
val valid4 = Bool()
|
||||
|
||||
val full4 = Bool()
|
||||
|
||||
val error3 = Bool()
|
||||
|
||||
val valid3 = Bool()
|
||||
|
||||
val full3 = Bool()
|
||||
|
||||
val error2 = Bool()
|
||||
|
||||
val valid2 = Bool()
|
||||
|
||||
val full2 = Bool()
|
||||
|
||||
val error1 = Bool()
|
||||
|
||||
val valid1 = Bool()
|
||||
|
||||
val full1 = Bool()
|
||||
|
||||
/* 1 when the debugger-to-core queue for serial port 0 has
|
||||
over or underflowed. This bit will remain set until it is reset by
|
||||
writing 1 to this bit.
|
||||
*/
|
||||
val error0 = Bool()
|
||||
|
||||
/* 1 when the core-to-debugger queue for serial port 0 is not empty.
|
||||
*/
|
||||
val valid0 = Bool()
|
||||
|
||||
/* 1 when the debugger-to-core queue for serial port 0 is full.
|
||||
*/
|
||||
val full0 = Bool()
|
||||
|
||||
}
|
||||
|
||||
class SERTXFields extends Bundle {
|
||||
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SERRXFields extends Bundle {
|
||||
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBCSFields extends Bundle {
|
||||
|
||||
val reserved0 = UInt(11.W)
|
||||
|
||||
/* When a 1 is written here, triggers a read at the address in {\tt
|
||||
sbaddress} using the access size set by \Fsbaccess.
|
||||
*/
|
||||
val sbsingleread = Bool()
|
||||
|
||||
/* Select the access size to use for system bus accesses triggered by
|
||||
writes to the {\tt sbaddress} registers or \Rsbdatazero.
|
||||
|
||||
0: 8-bit
|
||||
|
||||
1: 16-bit
|
||||
|
||||
2: 32-bit
|
||||
|
||||
3: 64-bit
|
||||
|
||||
4: 128-bit
|
||||
|
||||
If an unsupported system bus access size is written here,
|
||||
the DM may not perform the access, or may perform the access
|
||||
with any access size.
|
||||
*/
|
||||
val sbaccess = UInt(3.W)
|
||||
|
||||
/* When 1, the internal address value (used by the system bus master)
|
||||
is incremented by the access size (in bytes) selected in \Fsbaccess
|
||||
after every system bus access.
|
||||
*/
|
||||
val sbautoincrement = Bool()
|
||||
|
||||
/* When 1, every read from \Rsbdatazero automatically triggers a system
|
||||
bus read at the new address.
|
||||
*/
|
||||
val sbautoread = Bool()
|
||||
|
||||
/* When the debug module's system bus
|
||||
master causes a bus error, this field gets set. The bits in this
|
||||
field remain set until they are cleared by writing 1 to them.
|
||||
While this field is non-zero, no more system bus accesses can be
|
||||
initiated by the debug module.
|
||||
|
||||
0: There was no bus error.
|
||||
|
||||
1: There was a timeout.
|
||||
|
||||
2: A bad address was accessed.
|
||||
|
||||
3: There was some other error (eg. alignment).
|
||||
|
||||
4: The system bus master was busy when a one of the
|
||||
{\tt sbaddress} or {\tt sbdata} registers was written,
|
||||
or the {\tt sbdata} register was read when it had
|
||||
stale data.
|
||||
*/
|
||||
val sberror = UInt(3.W)
|
||||
|
||||
/* Width of system bus addresses in bits. (0 indicates there is no bus
|
||||
access support.)
|
||||
*/
|
||||
val sbasize = UInt(7.W)
|
||||
|
||||
/* 1 when 128-bit system bus accesses are supported.
|
||||
*/
|
||||
val sbaccess128 = Bool()
|
||||
|
||||
/* 1 when 64-bit system bus accesses are supported.
|
||||
*/
|
||||
val sbaccess64 = Bool()
|
||||
|
||||
/* 1 when 32-bit system bus accesses are supported.
|
||||
*/
|
||||
val sbaccess32 = Bool()
|
||||
|
||||
/* 1 when 16-bit system bus accesses are supported.
|
||||
*/
|
||||
val sbaccess16 = Bool()
|
||||
|
||||
/* 1 when 8-bit system bus accesses are supported.
|
||||
*/
|
||||
val sbaccess8 = Bool()
|
||||
|
||||
}
|
||||
|
||||
class SBADDRESS0Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 31:0 of the internal address.
|
||||
*/
|
||||
val address = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBADDRESS1Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 63:32 of the internal address (if the system address
|
||||
bus is that wide).
|
||||
*/
|
||||
val address = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBADDRESS2Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 95:64 of the internal address (if the system address
|
||||
bus is that wide).
|
||||
*/
|
||||
val address = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBDATA0Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 31:0 of the internal data.
|
||||
*/
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBDATA1Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 63:32 of the internal data (if the system bus is
|
||||
that wide).
|
||||
*/
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBDATA2Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 95:64 of the internal data (if the system bus is
|
||||
that wide).
|
||||
*/
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
||||
class SBDATA3Fields extends Bundle {
|
||||
|
||||
/* Accesses bits 127:96 of the internal data (if the system bus is
|
||||
that wide).
|
||||
*/
|
||||
val data = UInt(32.W)
|
||||
|
||||
}
|
||||
|
92
src/main/scala/devices/tilelink/Clint.scala
Normal file
92
src/main/scala/devices/tilelink/Clint.scala
Normal file
@ -0,0 +1,92 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.regmapper._
|
||||
import freechips.rocketchip.tile.XLen
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
import scala.math.{min,max}
|
||||
|
||||
object ClintConsts
|
||||
{
|
||||
def msipOffset(hart: Int) = hart * msipBytes
|
||||
def timecmpOffset(hart: Int) = 0x4000 + hart * timecmpBytes
|
||||
def timeOffset = 0xbff8
|
||||
def msipBytes = 4
|
||||
def timecmpBytes = 8
|
||||
def size = 0x10000
|
||||
def timeWidth = 64
|
||||
def regWidth = 32
|
||||
def ints = 2
|
||||
}
|
||||
|
||||
case class ClintParams(baseAddress: BigInt = 0x02000000)
|
||||
{
|
||||
def address = AddressSet(baseAddress, ClintConsts.size-1)
|
||||
}
|
||||
|
||||
class CoreplexLocalInterrupter(params: ClintParams)(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
import ClintConsts._
|
||||
|
||||
// clint0 => at most 4095 devices
|
||||
val device = new SimpleDevice("clint", Seq("riscv,clint0")) {
|
||||
override val alwaysExtended = true
|
||||
}
|
||||
|
||||
val node = TLRegisterNode(
|
||||
address = Seq(params.address),
|
||||
device = device,
|
||||
beatBytes = p(XLen)/8)
|
||||
|
||||
val intnode = IntNexusNode(
|
||||
numSourcePorts = 0 to 1024,
|
||||
numSinkPorts = 0 to 0,
|
||||
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(ints, Seq(Resource(device, "int"))))) },
|
||||
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val rtcTick = Bool(INPUT)
|
||||
val int = intnode.bundleOut
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
val time = Seq.fill(timeWidth/regWidth)(Reg(init=UInt(0, width = regWidth)))
|
||||
when (io.rtcTick) {
|
||||
val newTime = time.asUInt + UInt(1)
|
||||
for ((reg, i) <- time zip (0 until timeWidth by regWidth))
|
||||
reg := newTime >> i
|
||||
}
|
||||
|
||||
val nTiles = intnode.edgesOut.size
|
||||
val timecmp = Seq.fill(nTiles) { Seq.fill(timeWidth/regWidth)(Reg(UInt(width = regWidth))) }
|
||||
val ipi = Seq.fill(nTiles) { RegInit(UInt(0, width = 1)) }
|
||||
|
||||
io.int.zipWithIndex.foreach { case (int, i) =>
|
||||
int(0) := ipi(i)(0) // msip
|
||||
int(1) := time.asUInt >= timecmp(i).asUInt // mtip
|
||||
}
|
||||
|
||||
/* 0000 msip hart 0
|
||||
* 0004 msip hart 1
|
||||
* 4000 mtimecmp hart 0 lo
|
||||
* 4004 mtimecmp hart 0 hi
|
||||
* 4008 mtimecmp hart 1 lo
|
||||
* 400c mtimecmp hart 1 hi
|
||||
* bff8 mtime lo
|
||||
* bffc mtime hi
|
||||
*/
|
||||
|
||||
def makeRegFields(s: Seq[UInt]) = s.map(r => RegField(regWidth, r))
|
||||
|
||||
node.regmap(
|
||||
0 -> makeRegFields(ipi),
|
||||
timecmpOffset(0) -> makeRegFields(timecmp.flatten),
|
||||
timeOffset -> makeRegFields(time))
|
||||
}
|
||||
}
|
57
src/main/scala/devices/tilelink/Error.scala
Normal file
57
src/main/scala/devices/tilelink/Error.scala
Normal file
@ -0,0 +1,57 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
class TLError(address: Seq[AddressSet], beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
val device = new SimpleDevice("error-device", Seq("sifive,error0"))
|
||||
|
||||
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||
Seq(TLManagerParameters(
|
||||
address = address,
|
||||
resources = device.reg("mem"),
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
supportsPutPartial = TransferSizes(1, beatBytes),
|
||||
supportsPutFull = TransferSizes(1, beatBytes),
|
||||
supportsArithmetic = TransferSizes(1, beatBytes),
|
||||
supportsLogical = TransferSizes(1, beatBytes),
|
||||
supportsHint = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0))), // requests are handled in order
|
||||
beatBytes = beatBytes,
|
||||
minLatency = 1))) // no bypass needed for this device
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
import TLMessages._
|
||||
val opcodes = Vec(AccessAck, AccessAck, AccessAckData, AccessAckData, AccessAckData, HintAck)
|
||||
|
||||
val in = io.in(0)
|
||||
val a = Queue(in.a, 1)
|
||||
val d = in.d
|
||||
|
||||
a.ready := d.ready
|
||||
d.valid := a.valid
|
||||
d.bits.opcode := opcodes(a.bits.opcode)
|
||||
d.bits.param := UInt(0)
|
||||
d.bits.size := a.bits.size
|
||||
d.bits.source := a.bits.source
|
||||
d.bits.sink := UInt(0)
|
||||
d.bits.addr_lo := a.bits.address
|
||||
d.bits.data := UInt(0)
|
||||
d.bits.error := a.bits.opcode =/= Hint // Hints may not error
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
233
src/main/scala/devices/tilelink/Plic.scala
Normal file
233
src/main/scala/devices/tilelink/Plic.scala
Normal file
@ -0,0 +1,233 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import Chisel.ImplicitConversions._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.regmapper._
|
||||
import freechips.rocketchip.tile.XLen
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
import scala.math.min
|
||||
|
||||
class GatewayPLICIO extends Bundle {
|
||||
val valid = Bool(OUTPUT)
|
||||
val ready = Bool(INPUT)
|
||||
val complete = Bool(INPUT)
|
||||
}
|
||||
|
||||
class LevelGateway extends Module {
|
||||
val io = new Bundle {
|
||||
val interrupt = Bool(INPUT)
|
||||
val plic = new GatewayPLICIO
|
||||
}
|
||||
|
||||
val inFlight = Reg(init=Bool(false))
|
||||
when (io.interrupt && io.plic.ready) { inFlight := true }
|
||||
when (io.plic.complete) { inFlight := false }
|
||||
io.plic.valid := io.interrupt && !inFlight
|
||||
}
|
||||
|
||||
object PLICConsts
|
||||
{
|
||||
def maxDevices = 1023
|
||||
def maxHarts = 15872
|
||||
def priorityBase = 0x0
|
||||
def pendingBase = 0x1000
|
||||
def enableBase = 0x2000
|
||||
def hartBase = 0x200000
|
||||
|
||||
def claimOffset = 4
|
||||
def priorityBytes = 4
|
||||
|
||||
def enableOffset(i: Int) = i * ((maxDevices+7)/8)
|
||||
def hartOffset(i: Int) = i * 0x1000
|
||||
def enableBase(i: Int):Int = enableOffset(i) + enableBase
|
||||
def hartBase(i: Int):Int = hartOffset(i) + hartBase
|
||||
|
||||
def size = hartBase(maxHarts)
|
||||
require(hartBase >= enableBase(maxHarts))
|
||||
}
|
||||
|
||||
case class PLICParams(baseAddress: BigInt = 0xC000000, maxPriorities: Int = 7)
|
||||
{
|
||||
require (maxPriorities >= 0)
|
||||
def address = AddressSet(baseAddress, PLICConsts.size-1)
|
||||
}
|
||||
|
||||
/** Platform-Level Interrupt Controller */
|
||||
class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
// plic0 => max devices 1023
|
||||
val device = new SimpleDevice("interrupt-controller", Seq("riscv,plic0")) {
|
||||
override val alwaysExtended = true
|
||||
override def describe(resources: ResourceBindings): Description = {
|
||||
val Description(name, mapping) = super.describe(resources)
|
||||
val extra = Map(
|
||||
"interrupt-controller" -> Nil,
|
||||
"riscv,ndev" -> Seq(ResourceInt(nDevices)),
|
||||
"#interrupt-cells" -> Seq(ResourceInt(1)))
|
||||
Description(name, mapping ++ extra)
|
||||
}
|
||||
}
|
||||
|
||||
val node = TLRegisterNode(
|
||||
address = Seq(params.address),
|
||||
device = device,
|
||||
beatBytes = p(XLen)/8,
|
||||
undefZero = true,
|
||||
concurrency = 1) // limiting concurrency handles RAW hazards on claim registers
|
||||
|
||||
val intnode = IntNexusNode(
|
||||
numSourcePorts = 0 to 1024,
|
||||
numSinkPorts = 0 to 1024,
|
||||
sourceFn = { _ => IntSourcePortParameters(Seq(IntSourceParameters(1, Seq(Resource(device, "int"))))) },
|
||||
sinkFn = { _ => IntSinkPortParameters(Seq(IntSinkParameters())) })
|
||||
|
||||
/* Negotiated sizes */
|
||||
def nDevices: Int = intnode.edgesIn.map(_.source.num).sum
|
||||
def nPriorities = min(params.maxPriorities, nDevices)
|
||||
def nHarts = intnode.edgesOut.map(_.source.num).sum
|
||||
|
||||
// Assign all the devices unique ranges
|
||||
lazy val sources = intnode.edgesIn.map(_.source)
|
||||
lazy val flatSources = (sources zip sources.map(_.num).scanLeft(0)(_+_).init).map {
|
||||
case (s, o) => s.sources.map(z => z.copy(range = z.range.offset(o)))
|
||||
}.flatten
|
||||
|
||||
ResourceBinding {
|
||||
flatSources.foreach { s => s.resources.foreach { r =>
|
||||
// +1 because interrupt 0 is reserved
|
||||
(s.range.start until s.range.end).foreach { i => r.bind(device, ResourceInt(i+1)) }
|
||||
} }
|
||||
}
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val tl_in = node.bundleIn
|
||||
val devices = intnode.bundleIn
|
||||
val harts = intnode.bundleOut
|
||||
}
|
||||
|
||||
// Compact the interrupt vector the same way
|
||||
val interrupts = (intnode.edgesIn zip io.devices).map { case (e, i) => i.take(e.source.num) }.flatten
|
||||
// This flattens the harts into an MSMSMSMSMS... or MMMMM.... sequence
|
||||
val harts = io.harts.flatten
|
||||
|
||||
println(s"Interrupt map (${nHarts} harts ${nDevices} interrupts):")
|
||||
flatSources.foreach { s =>
|
||||
// +1 because 0 is reserved, +1-1 because the range is half-open
|
||||
println(s" [${s.range.start+1}, ${s.range.end}] => ${s.name}")
|
||||
}
|
||||
println("")
|
||||
|
||||
require (nDevices == interrupts.size)
|
||||
require (nHarts == harts.size)
|
||||
|
||||
require(nDevices <= PLICConsts.maxDevices)
|
||||
require(nHarts > 0 && nHarts <= PLICConsts.maxHarts)
|
||||
|
||||
// For now, use LevelGateways for all TL2 interrupts
|
||||
val gateways = Vec((false.B +: interrupts).map { case i =>
|
||||
val gateway = Module(new LevelGateway)
|
||||
gateway.io.interrupt := i
|
||||
gateway.io.plic
|
||||
})
|
||||
|
||||
val priority =
|
||||
if (nPriorities > 0) Reg(Vec(nDevices+1, UInt(width=log2Up(nPriorities+1))))
|
||||
else Wire(init=Vec.fill(nDevices+1)(UInt(1)))
|
||||
val threshold =
|
||||
if (nPriorities > 0) Reg(Vec(nHarts, UInt(width = log2Up(nPriorities+1))))
|
||||
else Wire(init=Vec.fill(nHarts)(UInt(0)))
|
||||
val pending = Reg(init=Vec.fill(nDevices+1){Bool(false)})
|
||||
val enables = Reg(Vec(nHarts, Vec(nDevices+1, Bool())))
|
||||
|
||||
def findMax(x: Seq[UInt]): (UInt, UInt) = {
|
||||
if (x.length > 1) {
|
||||
val half = 1 << (log2Ceil(x.length) - 1)
|
||||
val left = findMax(x take half)
|
||||
val right = findMax(x drop half)
|
||||
MuxT(left._1 >= right._1, left, (right._1, UInt(half) | right._2))
|
||||
} else (x.head, UInt(0))
|
||||
}
|
||||
|
||||
val maxDevs = Reg(Vec(nHarts, UInt(width = log2Up(pending.size))))
|
||||
for (hart <- 0 until nHarts) {
|
||||
val effectivePriority = (UInt(1) << priority(0).getWidth) +:
|
||||
(for (((p, en), pri) <- (pending zip enables(hart) zip priority).tail)
|
||||
yield Cat(p && en, pri))
|
||||
val (maxPri, maxDev) = findMax(effectivePriority)
|
||||
|
||||
maxDevs(hart) := maxDev
|
||||
harts(hart) := Reg(next = maxPri) > Cat(UInt(1), threshold(hart))
|
||||
}
|
||||
|
||||
def priorityRegField(x: UInt) = if (nPriorities > 0) RegField(32, x) else RegField.r(32, x)
|
||||
val priorityRegFields = Seq(PLICConsts.priorityBase -> priority.map(p => priorityRegField(p)))
|
||||
val pendingRegFields = Seq(PLICConsts.pendingBase -> pending .map(b => RegField.r(1, b)))
|
||||
|
||||
val enableRegFields = enables.zipWithIndex.map { case (e, i) =>
|
||||
PLICConsts.enableBase(i) -> e.map(b => RegField(1, b))
|
||||
}
|
||||
|
||||
// When a hart reads a claim/complete register, then the
|
||||
// device which is currently its highest priority is no longer pending.
|
||||
// This code exploits the fact that, practically, only one claim/complete
|
||||
// register can be read at a time. We check for this because if the address map
|
||||
// were to change, it may no longer be true.
|
||||
// Note: PLIC doesn't care which hart reads the register.
|
||||
val claimer = Wire(Vec(nHarts, Bool()))
|
||||
assert((claimer.asUInt & (claimer.asUInt - UInt(1))) === UInt(0)) // One-Hot
|
||||
val claiming = Vec.tabulate(nHarts){i => Mux(claimer(i), UIntToOH(maxDevs(i), nDevices+1), UInt(0))}
|
||||
val claimedDevs = Vec(claiming.reduceLeft( _ | _ ).toBools)
|
||||
|
||||
((pending zip gateways) zip claimedDevs) foreach { case ((p, g), c) =>
|
||||
g.ready := !p
|
||||
when (c || g.valid) { p := !c }
|
||||
}
|
||||
|
||||
// When a hart writes a claim/complete register, then
|
||||
// the written device (as long as it is actually enabled for that
|
||||
// hart) is marked complete.
|
||||
// This code exploits the fact that, practically, only one claim/complete register
|
||||
// can be written at a time. We check for this because if the address map
|
||||
// were to change, it may no longer be true.
|
||||
// Note -- PLIC doesn't care which hart writes the register.
|
||||
val completer = Wire(Vec(nHarts, Bool()))
|
||||
assert((completer.asUInt & (completer.asUInt - UInt(1))) === UInt(0)) // One-Hot
|
||||
val completerDev = Wire(UInt(width = log2Up(nDevices + 1)))
|
||||
val completedDevs = Mux(completer.reduce(_ || _), UIntToOH(completerDev, nDevices+1), UInt(0))
|
||||
(gateways zip completedDevs.toBools) foreach { case (g, c) =>
|
||||
g.complete := c
|
||||
}
|
||||
|
||||
val hartRegFields = Seq.tabulate(nHarts) { i =>
|
||||
PLICConsts.hartBase(i) -> Seq(
|
||||
priorityRegField(threshold(i)),
|
||||
RegField(32,
|
||||
RegReadFn { valid =>
|
||||
claimer(i) := valid
|
||||
(Bool(true), maxDevs(i))
|
||||
},
|
||||
RegWriteFn { (valid, data) =>
|
||||
assert(completerDev === data.extract(log2Ceil(nDevices+1)-1, 0),
|
||||
"completerDev should be consistent for all harts")
|
||||
completerDev := data.extract(log2Ceil(nDevices+1)-1, 0)
|
||||
completer(i) := valid && enables(i)(completerDev)
|
||||
Bool(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
node.regmap((priorityRegFields ++ pendingRegFields ++ enableRegFields ++ hartRegFields):_*)
|
||||
|
||||
priority(0) := 0
|
||||
pending(0) := false
|
||||
for (e <- enables)
|
||||
e(0) := false
|
||||
}
|
||||
}
|
52
src/main/scala/devices/tilelink/Rom.scala
Normal file
52
src/main/scala/devices/tilelink/Rom.scala
Normal file
@ -0,0 +1,52 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
// See LICENSE.Berkeley for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4,
|
||||
resources: Seq[Resource] = new SimpleDevice("rom", Seq("sifive,rom0")).reg("mem"))(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
|
||||
val node = TLManagerNode(beatBytes, TLManagerParameters (
|
||||
address = List(AddressSet(base, size-1)),
|
||||
resources = resources,
|
||||
regionType = RegionType.UNCACHED,
|
||||
executable = executable,
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0)))
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
val contents = contentsDelayed
|
||||
val wrapSize = 1 << log2Ceil(contents.size)
|
||||
require (wrapSize <= size)
|
||||
|
||||
val in = io.in(0)
|
||||
val edge = node.edgesIn(0)
|
||||
|
||||
val words = (contents ++ Seq.fill(wrapSize-contents.size)(0.toByte)).grouped(beatBytes).toSeq
|
||||
val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8})
|
||||
val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes)))
|
||||
|
||||
in.d.valid := in.a.valid
|
||||
in.a.ready := in.d.ready
|
||||
|
||||
val index = in.a.bits.address(log2Ceil(wrapSize)-1,log2Ceil(beatBytes))
|
||||
val high = if (wrapSize == size) UInt(0) else in.a.bits.address(log2Ceil(size)-1, log2Ceil(wrapSize))
|
||||
in.d.bits := edge.AccessAck(in.a.bits, UInt(0), Mux(high.orR, UInt(0), rom(index)))
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
84
src/main/scala/devices/tilelink/TestRAM.scala
Normal file
84
src/main/scala/devices/tilelink/TestRAM.scala
Normal file
@ -0,0 +1,84 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
// Do not use this for synthesis! Only for simulation.
|
||||
class TLTestRAM(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
val device = new MemoryDevice
|
||||
|
||||
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||
Seq(TLManagerParameters(
|
||||
address = List(address),
|
||||
resources = device.reg,
|
||||
regionType = RegionType.UNCACHED,
|
||||
executable = executable,
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
supportsPutPartial = TransferSizes(1, beatBytes),
|
||||
supportsPutFull = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0))), // requests are handled in order
|
||||
beatBytes = beatBytes)))
|
||||
|
||||
// We require the address range to include an entire beat (for the write mask)
|
||||
require ((address.mask & (beatBytes-1)) == beatBytes-1)
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
def bigBits(x: BigInt, tail: List[Boolean] = List.empty[Boolean]): List[Boolean] =
|
||||
if (x == 0) tail.reverse else bigBits(x >> 1, ((x & 1) == 1) :: tail)
|
||||
val mask = bigBits(address.mask >> log2Ceil(beatBytes))
|
||||
|
||||
val in = io.in(0)
|
||||
val edge = node.edgesIn(0)
|
||||
|
||||
val addrBits = (mask zip edge.addr_hi(in.a.bits).toBools).filter(_._1).map(_._2)
|
||||
val memAddress = Cat(addrBits.reverse)
|
||||
val mem = Mem(1 << addrBits.size, Vec(beatBytes, Bits(width = 8)))
|
||||
|
||||
// "Flow control"
|
||||
in.a.ready := in.d.ready
|
||||
in.d.valid := in.a.valid
|
||||
|
||||
val hasData = edge.hasData(in.a.bits)
|
||||
val wdata = Vec.tabulate(beatBytes) { i => in.a.bits.data(8*(i+1)-1, 8*i) }
|
||||
|
||||
in.d.bits := edge.AccessAck(in.a.bits, UInt(0))
|
||||
in.d.bits.data := Cat(mem(memAddress).reverse)
|
||||
in.d.bits.opcode := Mux(hasData, TLMessages.AccessAck, TLMessages.AccessAckData)
|
||||
when (in.a.fire() && hasData) { mem.write(memAddress, wdata, in.a.bits.mask.toBools) }
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
||||
|
||||
/** Synthesizeable unit testing */
|
||||
import freechips.rocketchip.unittest._
|
||||
|
||||
class TLRAMZeroDelay(ramBeatBytes: Int, txns: Int)(implicit p: Parameters) extends LazyModule {
|
||||
val fuzz = LazyModule(new TLFuzzer(txns))
|
||||
val model = LazyModule(new TLRAMModel("ZeroDelay"))
|
||||
val ram = LazyModule(new TLTestRAM(AddressSet(0x0, 0x3ff), beatBytes = ramBeatBytes))
|
||||
|
||||
model.node := fuzz.node
|
||||
ram.node := TLDelayer(0.25)(model.node)
|
||||
|
||||
lazy val module = new LazyModuleImp(this) with HasUnitTestIO {
|
||||
io.finished := fuzz.module.io.finished
|
||||
}
|
||||
}
|
||||
|
||||
class TLRAMZeroDelayTest(ramBeatBytes: Int, txns: Int = 5000, timeout: Int = 500000)(implicit p: Parameters) extends UnitTest(timeout) {
|
||||
io.finished := Module(LazyModule(new TLRAMZeroDelay(ramBeatBytes, txns)).module).io.finished
|
||||
}
|
48
src/main/scala/devices/tilelink/Zero.scala
Normal file
48
src/main/scala/devices/tilelink/Zero.scala
Normal file
@ -0,0 +1,48 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
|
||||
class TLZero(address: AddressSet, executable: Boolean = true, beatBytes: Int = 4)(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
val device = new SimpleDevice("rom", Seq("ucbbar,cacheable-zero0"))
|
||||
|
||||
val node = TLManagerNode(Seq(TLManagerPortParameters(
|
||||
Seq(TLManagerParameters(
|
||||
address = List(address),
|
||||
resources = device.reg("mem"),
|
||||
regionType = RegionType.UNCACHED,
|
||||
executable = executable,
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
supportsPutPartial = TransferSizes(1, beatBytes),
|
||||
supportsPutFull = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0))), // requests are handled in order
|
||||
beatBytes = beatBytes,
|
||||
minLatency = 1))) // no bypass needed for this device
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
val in = io.in(0)
|
||||
val edge = node.edgesIn(0)
|
||||
|
||||
val a = Queue(in.a, 2)
|
||||
val hasData = edge.hasData(a.bits)
|
||||
|
||||
a.ready := in.d.ready
|
||||
in.d.valid := a.valid
|
||||
in.d.bits := edge.AccessAck(a.bits, UInt(0))
|
||||
in.d.bits.opcode := Mux(hasData, TLMessages.AccessAck, TLMessages.AccessAckData)
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user