Имитация дизайна процессора, написанного на долоте - PullRequest
5 голосов
/ 09 апреля 2019

Я написал одноцилиндровый ЦП в Chisel3, который реализует большинство инструкций RV32I (кроме CSR, Fence, ECALL / BREAK, LB / SB, которые могут быть включены позже).В настоящее время инструкции жестко запрограммированы в памяти инструкций, но я изменю это, чтобы вместо них они считывали инструкции из файла.У меня возникли проблемы с тем, как на самом деле имитировать мой дизайн.Вот код, где я «склеил» все компоненты вместе.

class Core extends Module {
  val io = IO(new Bundle {
    val dc = Input(Bool())
})
io := DontCare

val pc          = RegInit(0.U)
val pcSelect    = Module(new PcSelect())
val pcPlusFour  = Module(new Adder())
val alu         = Module(new ALU())
val aluControl  = Module(new AluControl())
val control     = Module(new Control())
val immGen      = Module(new ImmGen())
val branchLogic = Module(new BranchLogic())
val branchUnit  = Module(new Adder())
val jumpReg     = Module(new JumpReg())
val regFile     = Module(new RegFile())
val jumpAdder   = Module(new Adder())
val dataMem     = Module(new DataMemory())
val instrMem    = Module(new InstructionMemory())

// Mux from data memory
val dataMux    = Mux(control.io.memToReg, dataMem.io.readDataOutput, alu.io.result)

// Mux to register file
val regFileMux = Mux(control.io.writeSrc, pcPlusFour.io.result, dataMux)

// PC + 4
pcPlusFour.io.in1 := pc
pcPlusFour.io.in2 := 4.U 

// Instruction memory
instrMem.io.address := pc
val instruction = instrMem.io.instruction
val opcode      = instruction(6, 0)

// Control
control.io.opcode := opcode

// Register file
regFile.io.readReg1  := instruction(19, 15) // rs1
regFile.io.readReg2  := instruction(24, 20) // rs2
regFile.io.writeReg  := instruction(11, 7)  // rd
regFile.io.regWrite  := control.io.regWrite
regFile.io.writeData := regFileMux

// ALU
val aluMux1 = Mux(control.io.aluSrc1, immGen.io.extendedU, regFile.io.readData1)
alu.io.in1 := aluMux1
val src = control.io.aluSrc2
val aluMux2 = Mux(src === 1.U, immGen.io.extendedI, Mux(src === 2.U, immGen.io.extendedS, Mux(src === 3.U, pc, regFile.io.readData2)))
alu.io.in2 := aluMux2
alu.io.aluOp := aluControl.io.output

// ALU control
aluControl.io.aluOp  := control.io.aluOp
aluControl.io.funct7 := instruction(31, 25)
aluControl.io.funct3 := instruction(14, 12) 

// Data Memory
dataMem.io.readAddress := alu.io.result
dataMem.io.writeData   := regFile.io.readData2
dataMem.io.memWrite    := control.io.memWrite
dataMem.io.memRead     := control.io.memRead

// Immediate generator
immGen.io.instr := instruction

// Branch logic
branchLogic.io.reg1 := regFile.io.readData1
branchLogic.io.reg2 := regFile.io.readData2
branchLogic.io.branch := control.io.branch
branchLogic.io.funct3 := instruction(14, 12)

// Jump reg
jumpReg.io.reg1 := regFile.io.readData1
jumpReg.io.imm  := immGen.io.extendedI

// Jump
jumpAdder.io.in1 := pc
jumpAdder.io.in2 := immGen.io.extendedJ

// Branch
branchUnit.io.in1 := pc
branchUnit.io.in2 := immGen.io.extendedB

// PC-select
pcSelect.io.pcPlus4      := pcPlusFour.io.result
pcSelect.io.branch       := branchUnit.io.result
pcSelect.io.jump         := jumpAdder.io.result
pcSelect.io.jalr         := jumpReg.io.output
pcSelect.io.branchSignal := branchLogic.io.result
pcSelect.io.jumpSignal   := control.io.jump
pcSelect.io.jalrSignal   := control.io.jumpReg 

pc := pcSelect.io.output
}

Итак, мои вопросы:

  1. Как я могу смоделировать этот дизайн, чтобы увидеть, что он выполняет все инструкции правильно?
  2. Я хотел бы запуститьэталон "дристоун" на нем для измерения производительности.Как я могу это сделать (это возможно?)?Я не уверен, как обрабатывать системные вызовы, если это необходимо.

Заранее спасибо!

1 Ответ

4 голосов
/ 10 апреля 2019

Отличный вопрос: есть несколько способов приблизиться к этому.

Обычный подход - взять сгенерированный Verilog от Chisel и написать собственный тестовый комплект для создания своего дизайна.Этот тестовый комплект может быть написан на C ++, Verilog, SystemVerilog или другом вашем любимом языке тестового набора / склеивания.

Этот подход используется Sodor (https://github.com/ucb-bar/riscv-sodor) и Rocket-Chip (https://github.com/freechipsproject/rocket-chip), с внешним кодом тестового набора, написанным на C ++, но способным взаимодействовать с симуляторами Verilog, такими как Verilator и VCS. Тестовая логика C ++ позволяет пользователю передавать тестовый двоичный файл через командную строку и затем загружатьдвоичное - через какую-то «магию» - в тестовую память. Это может быть внешний интерфейс отладки, последовательный интерфейс Tether или предоставленная модель внешнего ОЗУ, которая может быть загружена тестовым жгутом (или что-то простое, что вы пишете сами, или что-то более сложное, например, dramsim2).

Этот материал довольно сложный, поэтому я рекомендую начать с простого: один из вариантов - создать в Chisel память черного ящика, которая будет поддерживаться простой памятью, котораяиспользует readmemh для инициализации себя. Хорошая особенность в том, что вам не нужно перекомпилировать кодчтобы запустить новые двоичные файлы, вы просто поменяете файл, который хотите загрузить, в память тестов.

Chisel также предоставляет свои собственные автономные тестеры, так что, возможно, вы можете выполнить тестовый набор целиком в Scala, ноЯ не видел, чтобы это было сделано для чего-то столь же сложного, как ядро, которое очень зависит от внешних раздражителей и потребности общаться с внешним миром.

...