Started RV32I core, theoretically done integer immediates
This commit is contained in:
commit
785578db9c
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
__pycache__
|
||||||
|
*.v
|
||||||
|
*.vcd
|
||||||
|
*.gtkw
|
105
core.py
Normal file
105
core.py
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
from nmigen import *
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
from opcodes import Opcodes
|
||||||
|
from instruction_decoder import InstructionDecoder
|
||||||
|
from regfile import RegisterFile
|
||||||
|
|
||||||
|
# TODO replace this with a proper wishbone bus
|
||||||
|
MemoryBus = Record([
|
||||||
|
("rw", 1),
|
||||||
|
("addr", 32),
|
||||||
|
("data", 32),
|
||||||
|
("i_valid", 1),
|
||||||
|
("o_ready", 1),
|
||||||
|
])
|
||||||
|
|
||||||
|
class IntImmediate(Enum):
|
||||||
|
ADDI = 0b000
|
||||||
|
SLTI = 0b010
|
||||||
|
SLTIU = 0b011
|
||||||
|
XORI = 0b100
|
||||||
|
ORI = 0b110
|
||||||
|
ANDI = 0b111
|
||||||
|
|
||||||
|
class RV32ICore(Elaboratable):
|
||||||
|
"""Basic RV32-I core."""
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.mem = MemoryBus
|
||||||
|
self.decoder = InstructionDecoder()
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
m.submodules.decoder = self.decoder
|
||||||
|
m.submodules.regfile = RegisterFile()
|
||||||
|
|
||||||
|
regfile = m.submodules.regfile
|
||||||
|
|
||||||
|
pc = Signal(unsigned(32))
|
||||||
|
instr = Signal(unsigned(32)) # Internal reg to hold instrunction data
|
||||||
|
r = Array([Signal(32) for _ in range(32)])
|
||||||
|
|
||||||
|
decoder_ports = self.decoder.ports()
|
||||||
|
funct = decoder_ports[1]
|
||||||
|
funct = decoder_ports[2]
|
||||||
|
imm = decoder_ports[3]
|
||||||
|
immu = decoder_ports[4]
|
||||||
|
src = decoder_ports[5]
|
||||||
|
dest = decoder_ports[6]
|
||||||
|
|
||||||
|
m.d.comb += self.decoder.instr.eq(instr)
|
||||||
|
|
||||||
|
m.d.sync += regfile.wen.eq(0)
|
||||||
|
|
||||||
|
with m.FSM():
|
||||||
|
with m.State("READ_PC"):
|
||||||
|
# Issue memory read to PC
|
||||||
|
m.d.sync += self.mem.rw.eq(0)
|
||||||
|
m.d.sync += self.mem.addr.eq(pc)
|
||||||
|
m.d.sync += self.mem.i_valid.eq(1)
|
||||||
|
m.next = "LOAD_PC"
|
||||||
|
|
||||||
|
with m.State("LOAD_PC"):
|
||||||
|
m.d.sync += self.mem.i_valid.eq(0)
|
||||||
|
|
||||||
|
with m.If(self.mem.o_ready):
|
||||||
|
m.d.sync += instr.eq(self.mem.data)
|
||||||
|
m.next = "DECODE"
|
||||||
|
|
||||||
|
with m.State("DECODE"):
|
||||||
|
with m.Switch(self.decoder.opcode):
|
||||||
|
with m.Case(Opcodes.OP_IMM):
|
||||||
|
m.next = "READ_PC"
|
||||||
|
|
||||||
|
m.d.comb += regfile.raddr1.eq(imm)
|
||||||
|
m.d.comb += regfile.waddr.eq(imm)
|
||||||
|
m.d.sync += regfile.wen.eq(1)
|
||||||
|
|
||||||
|
with m.Switch(funct):
|
||||||
|
with m.Case(IntImmediate.ADDI):
|
||||||
|
m.d.sync += regfile.wdata.eq(regfile.raddr1 + imm)
|
||||||
|
|
||||||
|
with m.Case(IntImmediate.SLTI):
|
||||||
|
m.d.sync += regfile.wdata.eq(regfile.raddr1 + imm)
|
||||||
|
|
||||||
|
with m.Case(IntImmediate.SLTIU):
|
||||||
|
m.d.sync += regfile.wdata.eq(regfile.raddr1 + immu)
|
||||||
|
|
||||||
|
with m.Case(IntImmediate.ANDI):
|
||||||
|
m.d.sync += regfile.wdata.eq(regfile.raddr1 & immu)
|
||||||
|
|
||||||
|
with m.Case(IntImmediate.ORI):
|
||||||
|
m.d.sync += regfile.wdata.eq(regfile.raddr1 | immu)
|
||||||
|
|
||||||
|
with m.Case(IntImmediate.XORI):
|
||||||
|
m.d.sync += regfile.wdata.eq(regfile.raddr1 ^ immu)
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from nmigen.cli import main
|
||||||
|
top = RV32ICore()
|
||||||
|
|
||||||
|
main(top)
|
42
instruction_decoder.py
Normal file
42
instruction_decoder.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
from nmigen import *
|
||||||
|
from opcodes import Opcodes
|
||||||
|
|
||||||
|
class InstructionDecoder(Elaboratable):
|
||||||
|
def __init__(self):
|
||||||
|
#### Input
|
||||||
|
self.instr = Signal(unsigned(32))
|
||||||
|
|
||||||
|
#### Output
|
||||||
|
self.opcode = Signal(Opcodes)
|
||||||
|
self.funct = Signal(unsigned(3))
|
||||||
|
self.imm = Signal(32)
|
||||||
|
self.immu = Signal(unsigned(32))
|
||||||
|
# Register selection
|
||||||
|
self.src = Signal(unsigned(5))
|
||||||
|
self.dest = Signal(unsigned(5))
|
||||||
|
self.base = Signal(unsigned(5))
|
||||||
|
|
||||||
|
def ports(self) -> tuple:
|
||||||
|
return (self.instr, self.opcode, self.funct, self.imm, self.immu, self.src, self.dest, self.base)
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
m.d.comb += self.opcode.eq(self.instr[0:6])
|
||||||
|
|
||||||
|
# TODO do actual sign extension
|
||||||
|
|
||||||
|
with m.Switch(self.opcode):
|
||||||
|
with m.Case(Opcodes.OP_IMM):
|
||||||
|
m.d.comb += self.imm[0:10].eq(self.instr[20:30])
|
||||||
|
for i in range(11, 32):
|
||||||
|
m.d.comb += self.imm[i].eq(self.instr[31])
|
||||||
|
|
||||||
|
m.d.comb += self.immu[0:11].eq(self.instr[20:31])
|
||||||
|
m.d.comb += self.immu[12:31].eq(0)
|
||||||
|
|
||||||
|
m.d.comb += self.funct.eq(self.instr[12:14])
|
||||||
|
m.d.comb += self.src.eq(self.instr[15:19])
|
||||||
|
m.d.comb += self.dest.eq(self.instr[7:11])
|
||||||
|
|
||||||
|
return m
|
40
opcodes.py
Normal file
40
opcodes.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class Opcodes(Enum):
|
||||||
|
"""List of all opcodes for RV32I instruction set"""
|
||||||
|
LOAD = 0b00000
|
||||||
|
LOAD_FP = 0b00001
|
||||||
|
CUSTOM_0 = 0b00010
|
||||||
|
MISC_MEM = 0b00011
|
||||||
|
OP_IMM = 0b00100
|
||||||
|
AUIPC = 0b00101
|
||||||
|
OP_IMM_32 = 0b00110
|
||||||
|
# 48b?
|
||||||
|
|
||||||
|
STORE = 0b01000
|
||||||
|
STORE_FP = 0b01001
|
||||||
|
CUSTOM_1 = 0b01010
|
||||||
|
AMO = 0b01011
|
||||||
|
OP = 0b01100
|
||||||
|
LUI = 0b01101
|
||||||
|
OP_32 = 0b01110
|
||||||
|
# 64b?
|
||||||
|
|
||||||
|
MADD = 0b10000
|
||||||
|
MSUB = 0b10001
|
||||||
|
NMSUB = 0b10010
|
||||||
|
NMADD = 0b10011
|
||||||
|
OP_FP = 0b10100
|
||||||
|
# reserved
|
||||||
|
CUSTOM_2 = 0b10110
|
||||||
|
# 48b?
|
||||||
|
|
||||||
|
BRANCH = 0b11000
|
||||||
|
JALR = 0b11001
|
||||||
|
# reserved
|
||||||
|
JAL = 0b11011
|
||||||
|
SYSTEM = 0b11100
|
||||||
|
# reserved
|
||||||
|
CUSTOM_3 = 0b11110
|
||||||
|
# >80b?
|
31
regfile.py
Normal file
31
regfile.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
from nmigen import *
|
||||||
|
|
||||||
|
class RegisterFile(Elaboratable):
|
||||||
|
def __init__(self):
|
||||||
|
self.regs = Array([Signal(32) for _ in range(32)])
|
||||||
|
|
||||||
|
self.wen = Signal()
|
||||||
|
self.waddr = Signal(unsigned(5))
|
||||||
|
self.raddr1 = Signal(unsigned(5))
|
||||||
|
self.raddr2 = Signal(unsigned(5))
|
||||||
|
self.wdata = Signal(unsigned(32))
|
||||||
|
self.rdata1 = Signal(unsigned(32))
|
||||||
|
self.rdata2 = Signal(unsigned(32))
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
with m.If(self.wen):
|
||||||
|
m.d.sync += self.regs[self.waddr].eq(self.wdata)
|
||||||
|
|
||||||
|
with m.If(self.raddr1 == 0):
|
||||||
|
m.d.comb += self.rdata1.eq(0)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += self.rdata1.eq(self.regs[self.raddr1])
|
||||||
|
|
||||||
|
with m.If(self.raddr2 == 0):
|
||||||
|
m.d.comb += self.rdata2.eq(0)
|
||||||
|
with m.Else():
|
||||||
|
m.d.comb += self.rdata2.eq(self.regs[self.raddr2])
|
||||||
|
|
||||||
|
return m
|
Loading…
Reference in New Issue
Block a user