Skip to content

Commit a5328f4

Browse files
author
Peter Birch
committed
Adding execution implementation for atomic instructions:
* Model now tracks atomic acquire and release * Single stage single HART implementations of LR, SC, and AMO* instructions now exist in insn.py * Basic test sequence for LR & SC defined in tests_atomic.py * Fixed issues with pytest tests
1 parent f27724c commit a5328f4

File tree

6 files changed

+189
-41
lines changed

6 files changed

+189
-41
lines changed

riscvmodel/insn.py

Lines changed: 133 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -487,85 +487,186 @@ def execute(self, model: Model):
487487
class InstructionLR(InstructionAMOType):
488488
""" Load reserved """
489489
def execute(self, model: Model):
490-
# TODO: implement
491-
pass
490+
# Perform a normal load
491+
data = model.state.memory.lw(model.state.intreg[self.rs1].unsigned())
492+
model.state.intreg[self.rd] = data
493+
# Perform correct lock or release actions
494+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
495+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
492496

493497

494498
@isa("sc", RV32A, opcode=0b0101111, funct5=0b00011, funct3=0b010)
495499
class InstructionSC(InstructionAMOType):
496500
""" Store conditional """
497501
def execute(self, model: Model):
498-
# TODO: implement
499-
pass
502+
# Check if this address is reserved
503+
if model.state.atomic_reserved(model.state.intreg[self.rs1]):
504+
model.state.memory.sw(
505+
model.state.intreg[self.rs1].unsigned(),
506+
model.state.intreg[self.rs2]
507+
)
508+
model.state.intreg[self.rd] = 0
509+
else:
510+
model.state.intreg[self.rd] = 1
511+
# Perform correct lock or release actions
512+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
513+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
500514

501515

502516
@isa("amoadd", RV32A, opcode=0b0101111, funct5=0b00000, funct3=0b010)
503517
class InstructionAMOADD(InstructionAMOType):
504-
""" Atomic swap operation """
518+
""" Atomic add operation """
505519
def execute(self, model: Model):
506-
# TODO: implement
507-
pass
520+
# This models a single HART with 1 stage pipeline, so will always succeed
521+
model.state.intreg[self.rd] = model.state.memory.lw(
522+
model.state.intreg[self.rs1].unsigned()
523+
)
524+
model.state.memory.sw(
525+
model.state.intreg[self.rs1].unsigned(),
526+
(model.state.intreg[self.rs2] + model.state.intreg[self.rd])
527+
)
528+
# Perform correct lock or release actions
529+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
530+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
508531

509532

510533
@isa("amoxor", RV32A, opcode=0b0101111, funct5=0b00100, funct3=0b010)
511534
class InstructionAMOXOR(InstructionAMOType):
512-
""" Atomic swap operation """
535+
""" Atomic XOR operation """
513536
def execute(self, model: Model):
514-
# TODO: implement
515-
pass
537+
# This models a single HART with 1 stage pipeline, so will always succeed
538+
model.state.intreg[self.rd] = model.state.memory.lw(
539+
model.state.intreg[self.rs1].unsigned()
540+
)
541+
model.state.memory.sw(
542+
model.state.intreg[self.rs1].unsigned(),
543+
(model.state.intreg[self.rs2] ^ model.state.intreg[self.rd])
544+
)
545+
# Perform correct lock or release actions
546+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
547+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
516548

517549

518550
@isa("amoor", RV32A, opcode=0b0101111, funct5=0b01000, funct3=0b010)
519551
class InstructionAMOOR(InstructionAMOType):
520-
""" Atomic swap operation """
552+
""" Atomic OR operation """
521553
def execute(self, model: Model):
522-
# TODO: implement
523-
pass
554+
# This models a single HART with 1 stage pipeline, so will always succeed
555+
model.state.intreg[self.rd] = model.state.memory.lw(
556+
model.state.intreg[self.rs1].unsigned()
557+
)
558+
model.state.memory.sw(
559+
model.state.intreg[self.rs1].unsigned(),
560+
(model.state.intreg[self.rs2] | model.state.intreg[self.rd])
561+
)
562+
# Perform correct lock or release actions
563+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
564+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
524565

525566

526567
@isa("amoand", RV32A, opcode=0b0101111, funct5=0b01100, funct3=0b010)
527568
class InstructionAMOAND(InstructionAMOType):
528-
""" Atomic swap operation """
569+
""" Atomic AND operation """
529570
def execute(self, model: Model):
530-
# TODO: implement
531-
pass
571+
# This models a single HART with 1 stage pipeline, so will always succeed
572+
model.state.intreg[self.rd] = model.state.memory.lw(
573+
model.state.intreg[self.rs1].unsigned()
574+
)
575+
model.state.memory.sw(
576+
model.state.intreg[self.rs1].unsigned(),
577+
(model.state.intreg[self.rs2] & model.state.intreg[self.rd])
578+
)
579+
# Perform correct lock or release actions
580+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
581+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
532582

533583

534584
@isa("amomin", RV32A, opcode=0b0101111, funct5=0b10000, funct3=0b010)
535585
class InstructionAMOMIN(InstructionAMOType):
536-
""" Atomic swap operation """
586+
""" Atomic minimum operation """
537587
def execute(self, model: Model):
538-
# TODO: implement
539-
pass
588+
# This models a single HART with 1 stage pipeline, so will always succeed
589+
model.state.intreg[self.rd] = model.state.memory.lw(
590+
model.state.intreg[self.rs1].unsigned()
591+
)
592+
model.state.memory.sw(
593+
model.state.intreg[self.rs1].unsigned(),
594+
min(model.state.intreg[self.rs2], model.state.intreg[self.rd])
595+
)
596+
# Perform correct lock or release actions
597+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
598+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
540599

541600

542601
@isa("amomax", RV32A, opcode=0b0101111, funct5=0b10100, funct3=0b010)
543602
class InstructionAMOMAX(InstructionAMOType):
544-
""" Atomic swap operation """
603+
""" Atomic maximum operation """
545604
def execute(self, model: Model):
546-
# TODO: implement
547-
pass
605+
# This models a single HART with 1 stage pipeline, so will always succeed
606+
model.state.intreg[self.rd] = model.state.memory.lw(
607+
model.state.intreg[self.rs1].unsigned()
608+
)
609+
model.state.memory.sw(
610+
model.state.intreg[self.rs1].unsigned(),
611+
max(model.state.intreg[self.rs2], model.state.intreg[self.rd])
612+
)
613+
# Perform correct lock or release actions
614+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
615+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
548616

549617

550618
@isa("amominu", RV32A, opcode=0b0101111, funct5=0b11000, funct3=0b010)
551619
class InstructionAMOMINU(InstructionAMOType):
552-
""" Atomic swap operation """
553-
def execute(self, model: Model):
554-
# TODO: implement
555-
pass
620+
""" Atomic unsigned minimum operation """
621+
def execute(self, model: Model):
622+
# This models a single HART with 1 stage pipeline, so will always succeed
623+
model.state.intreg[self.rd] = model.state.memory.lw(
624+
model.state.intreg[self.rs1].unsigned()
625+
)
626+
model.state.memory.sw(
627+
model.state.intreg[self.rs1].unsigned(),
628+
min(
629+
model.state.intreg[self.rs2].unsigned(),
630+
model.state.intreg[self.rd].unsigned()
631+
)
632+
)
633+
# Perform correct lock or release actions
634+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
635+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
556636

557637

558638
@isa("amomaxu", RV32A, opcode=0b0101111, funct5=0b11100, funct3=0b010)
559639
class InstructionAMOMAXU(InstructionAMOType):
560-
""" Atomic swap operation """
561-
def execute(self, model: Model):
562-
# TODO: implement
563-
pass
640+
""" Atomic unsigned maximum operation """
641+
def execute(self, model: Model):
642+
# This models a single HART with 1 stage pipeline, so will always succeed
643+
model.state.intreg[self.rd] = model.state.memory.lw(
644+
model.state.intreg[self.rs1].unsigned()
645+
)
646+
model.state.memory.sw(
647+
model.state.intreg[self.rs1].unsigned(),
648+
max(
649+
model.state.intreg[self.rs2].unsigned(),
650+
model.state.intreg[self.rd].unsigned()
651+
)
652+
)
653+
# Perform correct lock or release actions
654+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
655+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])
564656

565657

566658
@isa("amoswap", RV32A, opcode=0b0101111, funct5=0b00001, funct3=0b010)
567659
class InstructionAMOSWAP(InstructionAMOType):
568660
""" Atomic swap operation """
569661
def execute(self, model: Model):
570-
# TODO: implement
571-
pass
662+
# This models a single HART with 1 stage pipeline, so will always succeed
663+
model.state.intreg[self.rd] = model.state.memory.lw(
664+
model.state.intreg[self.rs1].unsigned()
665+
)
666+
model.state.memory.sw(
667+
model.state.intreg[self.rs1].unsigned(),
668+
model.state.intreg[self.rs2]
669+
)
670+
# Perform correct lock or release actions
671+
if self.rl: model.state.atomic_release(model.state.intreg[self.rs1])
672+
elif self.aq: model.state.atomic_acquire(model.state.intreg[self.rs1])

riscvmodel/isa.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -851,10 +851,3 @@ def get_mnemomics():
851851
"""
852852
return [i.mnemonic for i in get_insns()]
853853

854-
class TerminateException(Exception):
855-
"""
856-
Exception that signal the termination of the program
857-
"""
858-
def __init__(self, returncode):
859-
super().__init__()
860-
self.returncode = returncode

riscvmodel/model.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
from .types import Register, RegisterFile, TracePC, TraceIntegerRegister, TraceMemory
66
from .program import Program
77

8+
class TerminateException(Exception):
9+
"""
10+
Exception that signal the termination of the program
11+
"""
12+
def __init__(self, returncode):
13+
super().__init__()
14+
self.returncode = returncode
15+
816
# todo: raise exception on memory misalignment
917
class State(object):
1018
single_regs = []
@@ -17,6 +25,8 @@ def __init__(self, variant: Variant):
1725
self.pc_update = Register(32)
1826
self.memory = Memory()
1927
self.initialized = True
28+
# Used for tracking atomic accesses
29+
self.reservations = []
2030

2131
def randomize(self):
2232
self.intreg.randomize()
@@ -44,10 +54,19 @@ def __setattr__(self, key, value):
4454
else:
4555
super().__setattr__(key, value)
4656

47-
4857
def __str__(self):
4958
return "{}".format(self.intreg)
5059

60+
def atomic_acquire(self, address):
61+
if not address in self.reservations:
62+
self.reservations.append(address)
63+
64+
def atomic_release(self, address):
65+
if address in self.reservations:
66+
self.reservations.remove(address)
67+
68+
def atomic_reserved(self, address):
69+
return address in self.reservations
5170

5271
class Memory(object):
5372
def __init__(self, *, base: int = 0, size: int = 2^32):

riscvmodel/program/tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from riscvmodel.isa import *
2+
from riscvmodel.insn import *
23
from riscvmodel.regnames import *
34
from riscvmodel.program import Program
45

riscvmodel/program/tests_atomic.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from riscvmodel.isa import *
2+
from riscvmodel.insn import *
3+
from riscvmodel.regnames import *
4+
from riscvmodel.program import Program
5+
6+
7+
class LRSCTest(Program):
8+
"""Basic test of LR & SC instructions"""
9+
def __init__(self):
10+
super().__init__([
11+
# Perform load-reserved and acquire lock
12+
InstructionLR(rs1=x0, rd=x1, aq=1, rl=0),
13+
# Perform store-conditional and release lock (should succeed)
14+
InstructionSC(rs1=x0, rs2=x0, rd=x2, aq=0, rl=1),
15+
# Perform second store-conditional to same address (should fail)
16+
InstructionSC(rs1=x0, rs2=x0, rd=x3, aq=0, rl=1),
17+
])
18+
19+
def expects(self) -> dict:
20+
return {
21+
x0: 0,
22+
x2: 0, # Due to successful SC (2nd instruction)
23+
x3: 1, # Due to failed SC (3rd instruction)
24+
}
25+

tests/test_model.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from riscvmodel.program.tests import *
2+
from riscvmodel.program.tests_atomic import *
23
from riscvmodel.model import Model
3-
from riscvmodel.variant import RV32I
4+
from riscvmodel.variant import RV32I, RV32A
45

56
import pytest
67

@@ -21,3 +22,11 @@ def test_model_addi():
2122
m = Model(RV32I)
2223
m.execute(pgm)
2324
check_model(m, pgm.expects())
25+
26+
27+
def test_model_lr_sc():
28+
"""Test if load-reserved and store-conditional sequence works"""
29+
pgm = LRSCTest()
30+
m = Model(RV32A)
31+
m.execute(pgm)
32+
check_model(m, pgm.expects())

0 commit comments

Comments
 (0)