This post is about riscv python model. The doc describes it as:
This is a python model of the RISC-V ISA. It is intended to be a resource for Python-based automated testing and verification. It is under development and not very useful yet, but can be used to generate random assembler codeThis is a python model of the RISC-V ISA. It is intended to be a resource for Python-based automated testing and verification. It is under development and not very useful yet, but can be used to generate random assembler code
It provides many utils for generating, encoding and decoding riscv instructions. For example, It can generate random rv instructions.
$ riscv-random-asm 100 -i add -i or -i slti
add x19, x5, x31
or x24, x19, x3
slti x4, x31, 1436
add x19, x15, x30
or x28, x25, x14
or x26, x19, x19
slti x26, x26, -1747
Another utility is decoding back opcode to asm
$ riscv-machinsn-decode hexstring 0x007938b3
sltu x17, x18, x7
Cool! Right?
But the most interesting part is “RISCV Model” which can work as golden mode.
Starting with Simulator
which defines the top level methods to load programs into program memory and initial memory contents and eventually run
to reset model and start running instructions from program memory.
class Simulator:
def __init__(self, model):
self.model = model
self.program = []
def load_program(self, program, *, address=0):
self.program = [i for i in program]
def load_data(self, data = "", *, address=0):
mem = self.model.state.memory.memory
for a in range(int(len(data)/4)):
mem[a] = struct.unpack("<L", data[a*4:(a+1)*4])[0]
def run(self, *, pc=0):
self.model.reset(pc=pc)
cnt = 0
while True:
try:
self.model.issue(self.program[int(self.model.state.pc)>>2])
In model.py
, Model
creates important component State
def issue(self, insn):
self.state.pc += 4
expected_pc = self.state.pc
insn.execute(self)
trace = self.state.changes()
if self.verbose is not False:
self.verbose_file.write(self.asm_tpl.format(str(insn), ", ".join([str(t) for t in trace])))
self.state.commit()
And State
defines two methods commit
and change
which handle the update to register file and memory
def changes(self):
c = self.intreg.changes()
if self.pc_update.value != self.pc.value + 4:
c.append(TracePC(self.pc_update.value))
c += self.memory.changes()
return c
def commit(self):
self.intreg.commit()
self.pc.set(self.pc_update.value)
self.memory.commit()
and Memory
does the actual memory update
def commit(self):
for update in self.memory_updates:
address = update.addr
base = address >> 2
offset = address & 0x3
if base not in self.memory:
self.memory[base] = randrange(0, 1 << 32)
data = update.data
if update.gran == TraceMemory.GRANULARITY.BYTE:
mask = ~(0xFF << (offset*8)) & 0xFFFFFFFF
data = (self.memory[base] & mask) | (data << (offset*8))
self.memory[base] = data
self.memory_updates = []
And for completeness, RegisterFile
updates registers with commit
def commit(self):
for t in self.regs_updates:
self.regs[t.id].set(t.value)
self.regs_updates.clear()