Skip to content

Commit

Permalink
Allow debug module to be at a base address other than 0 (#2649)
Browse files Browse the repository at this point in the history
  • Loading branch information
ernie-sifive authored Oct 2, 2020
1 parent d894612 commit 9699c48
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 53 deletions.
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}
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

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

0 comments on commit 9699c48

Please sign in to comment.