Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow debug module to be at a base address other than 0 #2649

Merged
merged 10 commits into from
Oct 2, 2020
23 changes: 12 additions & 11 deletions scripts/debug_rom/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,28 @@
CC = $(RISCV)/bin/riscv64-unknown-elf-gcc
OBJCOPY = $(RISCV)/bin/riscv64-unknown-elf-objcopy

COMPILE = $(CC) -nostdlib -nostartfiles -I$(RISCV)/include/ -Tlink.ld
COMPILE = $(CC) -nostdlib -nostartfiles -I$(RISCV)/riscv64-unknown-elf/include/ -Tlink.ld

ELFS = debug_rom
DEPS = debug_rom.S link.ld
ELFS = debug_rom debug_rom_nonzero

all: $(patsubst %,%.h,$(ELFS))
all: $(ELFS)

publish: debug_rom.scala
mv $< ../../src/main/scala/devices/debug/DebugRomContents.scala
publish: DebugRomContents.scala DebugRomNonzeroContents.scala
mv $^ ../../src/main/scala/devices/debug

%.scala: %.raw
xxd -i $^ | sed -e "s/^unsigned char debug_rom_raw\[\] = {/\/\/ This file was auto-generated by 'make publish' in debug\/ directory.\n\npackage freechips.rocketchip.devices.debug\n\nobject DebugRomContents {\n\n def apply() : Array[Byte] = { Array (/" \
xxd -i $^ | sed -e "s/^unsigned char DebugRom.*/\/\/ This file was auto-generated by 'make publish' in debug\/ directory.\n\npackage freechips.rocketchip.devices.debug\n\nobject $(basename $<) {\n\n def apply() : Array[Byte] = { Array (/" \
-e "s/};/ ).map(_.toByte) }\n\n}/" \
-e "s/^unsigned int debug_rom_raw_len.*//" > $@
-e "s/^unsigned int DebugRom.*//" > $@

DebugRomContents.raw: debug_rom
$(OBJCOPY) -O binary --only-section .text $^ $@

%.raw: %
DebugRomNonzeroContents.raw: debug_rom_nonzero
$(OBJCOPY) -O binary --only-section .text $^ $@

debug_rom: $(DEPS)
%: %.S
$(COMPILE) -o $@ $^

clean:
rm -f $(ELFS) debug_rom*.raw debug_rom*.h
rm -f $(ELFS) DebugRom*.raw
2 changes: 1 addition & 1 deletion scripts/debug_rom/debug_rom.S
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// See LICENSE.SiFive for license details.

#include "spike/encoding.h"
#include "riscv-pk/encoding.h"

// These are implementation-specific addresses in the Debug Module
#define HALTED 0x100
Expand Down
89 changes: 89 additions & 0 deletions scripts/debug_rom/debug_rom_nonzero.S
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// See LICENSE.SiFive for license details.

#include "riscv-pk/encoding.h"

#ifndef CSR_DSCRATCH1
#define CSR_DSCRATCH1 (CSR_DSCRATCH + 1)
#endif

// These are implementation-specific addresses in the Debug Module, relative to entry
#define HALTED -0x700 // 0x100
#define GOING -0x6FC // 0x104
#define RESUMING -0x6F8 // 0x108
#define EXCEPTION -0x6F4 // 0x10C

// Region of memory where each hart has 1 byte to read.
#define FLAGS -0x400 // 0x400
#define FLAG_GO 0
#define FLAG_RESUME 1

.option norvc
.global entry
.global exception

// Entry location on ebreak, Halt, or Breakpoint
// It is the same for all harts. They branch when
// their GO or RESUME bit is set.

// 0x800
entry:
csrw CSR_DSCRATCH1, s1 // s1=s1, ds1=s1: Save s1 to use as base pointer
j _entry

// 0x808
exception:
csrrw s1, CSR_DSCRATCH1, s1 // s1=base, ds1=s1
sw zero, EXCEPTION(s1) // Let debug module know you got an exception.

_entry:
// This fence is required because the execution may have written something
// into the Abstract Data or Program Buffer registers.
fence
csrw CSR_DSCRATCH, s0 // Save s0 to allow signaling MHARTID
la s1, entry // s1 = base (0x800 into debug region)

// We continue to let the hart know that we are halted in order that
// a DM which was reset is still made aware that a hart is halted.
// We keep checking both whether there is something the debugger wants
// us to do, or whether we should resume.
entry_loop:
csrr s0, CSR_MHARTID
sw s0, HALTED(s1)
add s0, s0, s1 // s0=base + hartid
lbu s0, FLAGS(s0) // 1 byte flag per hart. Only one hart advances here.
andi s0, s0, (1 << FLAG_GO) | (1 << FLAG_RESUME)
beqz s0, entry_loop // Loop until either GO or RESUME is set.

andi s0, s0, (1 << FLAG_GO)
beqz s0, _resume // If GO is clear at this point, RESUME must be set.

csrr s0, CSR_DSCRATCH // Restore s0 here

//TODO: 0x48 is a hack for (icache-entry) constant
jalr zero, 0x48(s1) // Rocket-Chip has a specific hack which is that jalr in
// Debug Mode will flush the I-Cache. We need that so that the
// remainder of the variable instructions will be what Debug Module
// intends.
icache:
sw zero, GOING(s1) // When debug module sees this write, the GO flag is reset.
csrrw s1, CSR_DSCRATCH1, s1 // s1=s1, ds1=base
j whereto

_resume:
csrr s0, CSR_MHARTID
sw s0, RESUMING(s1) // When Debug Module sees this write, the RESUME flag is reset.
csrr s0, CSR_DSCRATCH // Restore s0
csrr s1, CSR_DSCRATCH1 // Restore s1
dret

// END OF ACTUAL "ROM" CONTENTS. BELOW IS JUST FOR LINKER SCRIPT.

.section .whereto
whereto:
nop
// Variable "ROM" This is : jal x0 abstract, jal x0 program_buffer,
// or jal x0 resume, as desired.
// Debug Module state machine tracks what is 'desired'.
// We don't need/want to use jalr here because all of the
// Variable ROM contents are set by
// Debug Module before setting the OK_GO byte.
141 changes: 103 additions & 38 deletions src/main/scala/devices/debug/Debug.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import chisel3.util._
import freechips.rocketchip.config._
import freechips.rocketchip.diplomacy._
import freechips.rocketchip.regmapper._
import freechips.rocketchip.rocket.Instructions
import freechips.rocketchip.rocket.{CSRs, Instructions}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder how much work it would be to move these two helpers to util so that Rocket and Debug can both depend on them, rather than having a circular dependency between these packages?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Instructions.scala file that has these objects looks like it used to be generated from the riscv/riscv-opcodes repository. I ran the chisel generator there, but the result was much different from Instructions.scala, so it's clear the two have diverged at some time in the past. The Spike CSR list used by the Debug ROM code is yet another divergent copy of this info. It makes sense to me to have this info in a foundation like riscv-opcodes and generate all uses from there.

import freechips.rocketchip.tile.MaxHartIdBits
import freechips.rocketchip.tilelink._
import freechips.rocketchip.devices.tilelink.{DevNullParams, TLError}
Expand Down Expand Up @@ -54,8 +54,8 @@ object DsbRegAddrs{
def IMPEBREAK(cfg: DebugModuleParams) = { DATA - 4 }

// We want abstract to be immediately before PROGBUF
// because we auto-generate 2 instructions.
def ABSTRACT(cfg:DebugModuleParams) = PROGBUF(cfg) - 8
// because we auto-generate 2 (or 5) instructions.
def ABSTRACT(cfg:DebugModuleParams) = PROGBUF(cfg) - (cfg.nAbstractInstructions * 4)

def FLAGS = 0x400
def ROMBASE = 0x800
Expand Down Expand Up @@ -101,6 +101,7 @@ object DebugAbstractCommandType extends scala.Enumeration {
**/

case class DebugModuleParams (
baseAddress : BigInt = BigInt(0),
nDMIAddrSize : Int = 7,
nProgramBufferWords: Int = 16,
nAbstractDataWords : Int = 4,
Expand Down Expand Up @@ -130,7 +131,12 @@ case class DebugModuleParams (
// TODO: Check that quick access requirements are met.
}

def address = AddressSet(0, 0xFFF) // This is required for correct functionality; it's not configurable.
def address = AddressSet(baseAddress, 0xFFF)
def atzero = (baseAddress == 0)
def nAbstractInstructions = if (atzero) 2 else 5
def debugEntry: BigInt = baseAddress + 0x800
def debugException: BigInt = baseAddress + 0x808
def nDscratch: Int = if (atzero) 1 else 2
}

object DefaultDebugModuleParams {
Expand Down Expand Up @@ -363,9 +369,12 @@ class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyMod
}

//----HARTINFO
// DATA registers are mapped to memory. The dataaddr field of HARTINFO has only
// 12 bits and assumes the DM base is 0. If not at 0, then HARTINFO reads as 0
// (implying nonexistence according to the Debug Spec).

val HARTINFORdData = WireInit(0.U.asTypeOf(new HARTINFOFields()))
when (dmAuthenticated) {
if (cfg.atzero) when (dmAuthenticated) {
HARTINFORdData.dataaccess := true.B
HARTINFORdData.datasize := cfg.nAbstractDataWords.U
HARTINFORdData.dataaddr := DsbRegAddrs.DATA.U
Expand Down Expand Up @@ -510,11 +519,11 @@ class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyMod
))

val hartinfoRegFields = RegFieldGroup("dmi_hartinfo", Some("hart information"), Seq(
RegField.r(12, HARTINFORdData.dataaddr, RegFieldDesc("dataaddr", "data address", reset=Some(DsbRegAddrs.DATA))),
RegField.r(4, HARTINFORdData.datasize, RegFieldDesc("datasize", "number of DATA registers", reset=Some(cfg.nAbstractDataWords))),
RegField.r(1, HARTINFORdData.dataaccess, RegFieldDesc("dataaccess", "data access type", reset=Some(1))),
RegField.r(12, HARTINFORdData.dataaddr, RegFieldDesc("dataaddr", "data address", reset=Some(if (cfg.atzero) DsbRegAddrs.DATA else 0))),
RegField.r(4, HARTINFORdData.datasize, RegFieldDesc("datasize", "number of DATA registers", reset=Some(if (cfg.atzero) cfg.nAbstractDataWords else 0))),
RegField.r(1, HARTINFORdData.dataaccess, RegFieldDesc("dataaccess", "data access type", reset=Some(if (cfg.atzero) 1 else 0))),
RegField(3),
RegField.r(4, HARTINFORdData.nscratch, RegFieldDesc("nscratch", "number of scratch registers", reset=Some(cfg.nScratch)))
RegField.r(4, HARTINFORdData.nscratch, RegFieldDesc("nscratch", "number of scratch registers", reset=Some(if (cfg.atzero) cfg.nScratch else 0)))
))

//--------------------------------------------------------------
Expand Down Expand Up @@ -1429,6 +1438,14 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
val opcode = UInt(7.W)
}

class GeneratedCSR extends Bundle {
val imm = UInt(12.W)
val rs1 = UInt(5.W)
val funct3 = UInt(3.W)
val rd = UInt(5.W)
val opcode = UInt(7.W)
}

class GeneratedUJ extends Bundle {
val imm3 = UInt(1.W)
val imm0 = UInt(10.W)
Expand All @@ -1451,41 +1468,87 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
}
}

val abstractGeneratedMem = Reg(Vec(2, (UInt(32.W))))
val abstractGeneratedI = Wire(new GeneratedI())
val abstractGeneratedS = Wire(new GeneratedS())
val nop = Wire(new GeneratedI())
require((cfg.atzero && cfg.nAbstractInstructions == 2) || (!cfg.atzero && cfg.nAbstractInstructions == 5),
"Mismatch between DebugModuleParams atzero and nAbstractInstructions")
val abstractGeneratedMem = Reg(Vec(cfg.nAbstractInstructions, (UInt(32.W))))

def abstractGeneratedI(cfg: DebugModuleParams): UInt = {
val inst = Wire(new GeneratedI())
val offset = if (cfg.atzero) DATA else (DATA-0x800) & 0xFFF
val base = if (cfg.atzero) 0.U else Mux(accessRegisterCommandReg.regno(0), 8.U, 9.U)
inst.opcode := (Instructions.LW.value.U.asTypeOf(new GeneratedI())).opcode
inst.rd := (accessRegisterCommandReg.regno & 0x1F.U)
inst.funct3 := accessRegisterCommandReg.size
inst.rs1 := base
inst.imm := offset.U
inst.asUInt
}

abstractGeneratedI.opcode := (Instructions.LW.value.U.asTypeOf(new GeneratedI())).opcode
abstractGeneratedI.rd := (accessRegisterCommandReg.regno & 0x1F.U)
abstractGeneratedI.funct3 := accessRegisterCommandReg.size
abstractGeneratedI.rs1 := 0.U
abstractGeneratedI.imm := DATA.U
def abstractGeneratedS(cfg: DebugModuleParams): UInt = {
val inst = Wire(new GeneratedS())
val offset = if (cfg.atzero) DATA else (DATA-0x800) & 0xFFF
val base = if (cfg.atzero) 0.U else Mux(accessRegisterCommandReg.regno(0), 8.U, 9.U)
inst.opcode := (Instructions.SW.value.U.asTypeOf(new GeneratedS())).opcode
inst.immlo := (offset & 0x1F).U
inst.funct3 := accessRegisterCommandReg.size
inst.rs1 := base
inst.rs2 := (accessRegisterCommandReg.regno & 0x1F.U)
inst.immhi := (offset >> 5).U
inst.asUInt
}

abstractGeneratedS.opcode := (Instructions.SW.value.U.asTypeOf(new GeneratedS())).opcode
abstractGeneratedS.immlo := (DATA & 0x1F).U
abstractGeneratedS.funct3 := accessRegisterCommandReg.size
abstractGeneratedS.rs1 := 0.U
abstractGeneratedS.rs2 := (accessRegisterCommandReg.regno & 0x1F.U)
abstractGeneratedS.immhi := (DATA >> 5).U
def abstractGeneratedCSR: UInt = {
val inst = Wire(new GeneratedCSR())
val base = Mux(accessRegisterCommandReg.regno(0), 8.U, 9.U) // use s0 as base for odd regs, s1 as base for even regs
inst := (Instructions.CSRRW.value.U.asTypeOf(new GeneratedCSR()))
inst.imm := CSRs.dscratch1.U
inst.rs1 := base
inst.rd := base
inst.asUInt
}

val nop = Wire(new GeneratedI())
nop := Instructions.ADDI.value.U.asTypeOf(new GeneratedI())
nop.rd := 0.U
nop.rs1 := 0.U
nop.imm := 0.U

val isa = Wire(new GeneratedI())
isa := Instructions.ADDIW.value.U.asTypeOf(new GeneratedI())
isa.rd := 0.U
isa.rs1 := 0.U
isa.imm := 0.U


when (goAbstract) {
abstractGeneratedMem(0) := Mux(accessRegisterCommandReg.transfer,
Mux(accessRegisterCommandReg.write,
// To write a register, we need to do LW.
abstractGeneratedI.asUInt(),
// To read a register, we need to do SW.
abstractGeneratedS.asUInt()),
nop.asUInt()
)
abstractGeneratedMem(1) := Mux(accessRegisterCommandReg.postexec,
nop.asUInt(),
Instructions.EBREAK.value.U)
if (cfg.nAbstractInstructions == 2) {
// ABSTRACT(0): Transfer: LW or SW, else NOP
// ABSTRACT(1): Postexec: NOP else EBREAK
abstractGeneratedMem(0) := Mux(accessRegisterCommandReg.transfer,
Mux(accessRegisterCommandReg.write, abstractGeneratedI(cfg), abstractGeneratedS(cfg)),
nop.asUInt()
)
abstractGeneratedMem(1) := Mux(accessRegisterCommandReg.postexec,
nop.asUInt(),
Instructions.EBREAK.value.U)
} else {
// Entry: All regs in GPRs, dscratch1=offset 0x800 in DM
// ABSTRACT(0): CheckISA: ADDW or NOP (exception here if size=3 and not RV64)
// ABSTRACT(1): CSRRW s1,dscratch1,s1 or CSRRW s0,dscratch1,s0
// ABSTRACT(2): Transfer: LW, SW, LD, SD else NOP
// ABSTRACT(3): CSRRW s1,dscratch1,s1 or CSRRW s0,dscratch1,s0
// ABSTRACT(4): Postexec: NOP else EBREAK
abstractGeneratedMem(0) := Mux(accessRegisterCommandReg.transfer && accessRegisterCommandReg.size =/= 2.U, isa.asUInt(), nop.asUInt())
abstractGeneratedMem(1) := abstractGeneratedCSR
abstractGeneratedMem(2) := Mux(accessRegisterCommandReg.transfer,
Mux(accessRegisterCommandReg.write, abstractGeneratedI(cfg), abstractGeneratedS(cfg)),
nop.asUInt()
)
abstractGeneratedMem(3) := abstractGeneratedCSR
abstractGeneratedMem(4) := Mux(accessRegisterCommandReg.postexec,
nop.asUInt(),
Instructions.EBREAK.value.U)
}
}

//--------------------------------------------------------------
Expand Down Expand Up @@ -1528,7 +1591,8 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
flags.zipWithIndex.map{case(x, i) => RegField.r(8, x.asUInt(), RegFieldDesc(s"debug_flags_$i", "", volatile=true))}
}),
ROMBASE -> RegFieldGroup("debug_rom", Some("Debug ROM"),
DebugRomContents().zipWithIndex.map{case (x, i) => RegField.r(8, (x & 0xFF).U(8.W), RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
(if (cfg.atzero) DebugRomContents() else DebugRomNonzeroContents()).zipWithIndex.map{case (x, i) =>
RegField.r(8, (x & 0xFF).U(8.W), RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
)

// Override System Bus accesses with dmactive reset.
Expand Down Expand Up @@ -1579,13 +1643,14 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
val commandWrIsAccessRegister = (COMMANDWrData.cmdtype === DebugAbstractCommandType.AccessRegister.id.U)
val commandRegIsAccessRegister = (COMMANDReg.cmdtype === DebugAbstractCommandType.AccessRegister.id.U)

val commandWrIsUnsupported = COMMANDWrEn && !commandWrIsAccessRegister;
val commandWrIsUnsupported = COMMANDWrEn && !commandWrIsAccessRegister
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a semi-colon?! that must be a @mwachs5 2017 original 😂


val commandRegIsUnsupported = WireInit(true.B)
val commandRegBadHaltResume = WireInit(false.B)

// We only support abstract commands for GPRs and any custom registers, if specified.
val accessRegIsGPR = (accessRegisterCommandReg.regno >= 0x1000.U && accessRegisterCommandReg.regno <= 0x101F.U)
val accessRegIsLegalSize = (accessRegisterCommandReg.size === 2.U) || (accessRegisterCommandReg.size === 3.U)
val accessRegIsGPR = (accessRegisterCommandReg.regno >= 0x1000.U && accessRegisterCommandReg.regno <= 0x101F.U) && accessRegIsLegalSize
val accessRegIsCustom = if (needCustom) {
val (custom, customP) = customNode.in.head
customP.addrs.foldLeft(false.B){
Expand Down
20 changes: 20 additions & 0 deletions src/main/scala/devices/debug/DebugRomNonzeroContents.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This file was auto-generated by 'make publish' in debug/ directory.

package freechips.rocketchip.devices.debug

object DebugRomNonzeroContents {

def apply() : Array[Byte] = { Array (
0x73, 0x90, 0x34, 0x7b, 0x6f, 0x00, 0xc0, 0x00, 0xf3, 0x94, 0x34, 0x7b,
0x23, 0xa6, 0x04, 0x90, 0x0f, 0x00, 0xf0, 0x0f, 0x73, 0x10, 0x24, 0x7b,
0x97, 0x04, 0x00, 0x00, 0x93, 0x84, 0x84, 0xfe, 0x73, 0x24, 0x40, 0xf1,
0x23, 0xa0, 0x84, 0x90, 0x33, 0x04, 0x94, 0x00, 0x03, 0x44, 0x04, 0xc0,
0x13, 0x74, 0x34, 0x00, 0xe3, 0x06, 0x04, 0xfe, 0x13, 0x74, 0x14, 0x00,
0x63, 0x0c, 0x04, 0x00, 0x73, 0x24, 0x20, 0x7b, 0x67, 0x80, 0x84, 0x04,
0x23, 0xa2, 0x04, 0x90, 0xf3, 0x94, 0x34, 0x7b, 0x6f, 0xf0, 0x1f, 0xab,
0x73, 0x24, 0x40, 0xf1, 0x23, 0xa4, 0x84, 0x90, 0x73, 0x24, 0x20, 0x7b,
0xf3, 0x24, 0x30, 0x7b, 0x73, 0x00, 0x20, 0x7b
).map(_.toByte) }

}

Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class DebugLogicalTreeNode(
hartSeltoHartIDMapping = Nil, // HartSel goes from 0->N but HartID is not contiguious or increasing
authenticationType = (if (cfg.hasAuthentication) PASSTHRU else NONE),
nHartsellenBits = p(MaxHartIdBits), // Number of actually implemented bits of Hartsel
hasHartInfo = true,
hasHartInfo = cfg.atzero,
hasAbstractauto = true,
cfgStrPtrValid = false,
nHaltSummaryRegisters = 2,
Expand Down
Loading