diff --git a/Makefile b/Makefile index 66e2400..1a87f0f 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ clean: include emulator/Makefile.mk pytest: - pytest -s + pytest -s --log-cli-level=DEBUG test: pytest test_verilog_modules diff --git a/planner/asm/line_parser.py b/planner/asm/line_parser.py index 99b7d5a..a23b1f2 100644 --- a/planner/asm/line_parser.py +++ b/planner/asm/line_parser.py @@ -1,9 +1,12 @@ -from enum import Enum -import re from typing import List, Tuple, Optional from planner import unit, util - +def address_of(op: unit.Operand): + if op == unit.Operand.CONSTANT: + return unit.Operand.ADDRESS + if op == unit.Operand.ADDRESS: + return unit.Operand.DADDRESS + raise AssertionError("can't take address of %s" % op) def parse_line(line: str) -> Tuple[Optional[str], Optional[List[Tuple[unit.Operand, unit.LazyLabel]]]]: try: @@ -28,16 +31,20 @@ def parse_line(line: str) -> Tuple[Optional[str], Optional[List[Tuple[unit.Opera op = op.strip() if len(op) == 0: raise ValueError("no-length operand found in %s" % line) + optype = unit.Operand.CONSTANT + if op.startswith("[") and op.endswith("]"): + optype = address_of(optype) + op = op[1:-1].strip() + if op.startswith("[") and op.endswith("]"): + optype = address_of(optype) + op = op[1:-1].strip() + if op.startswith("R"): if len(op) != 2 or op[1] not in "0123456789": raise ValueError("Invalid mem-register {op} provided, only R0..R9 supported.") - optype = unit.Operand.ADDRESS - op = op[1:] - elif op.startswith("[") and op.endswith("]"): - optype = unit.Operand.ADDRESS - op = op[1:-1].strip() - else: - optype = unit.Operand.CONSTANT + optype = address_of(optype) + op = str(int(op[1:])*4) + try: value = unit.LazyLabel(util.LABEL_CONSTANT, int(op, 0)) # automatically understand base-10 and base-16 except ValueError as e: diff --git a/planner/asm/line_parser_test.py b/planner/asm/line_parser_test.py index a658542..7ec4b40 100644 --- a/planner/asm/line_parser_test.py +++ b/planner/asm/line_parser_test.py @@ -30,10 +30,14 @@ def test_parse_line_success(self): (unit.Operand.ADDRESS, 22), (unit.Operand.ADDRESS, 15)]) + name, tokens = line_parser.parse_line("load [ 0x16 ] , [[15 ]]") + self.assertEqual(name, "LOAD") + self.assertEqual(tokens, [ + (unit.Operand.ADDRESS, 22), + (unit.Operand.DADDRESS, 15)]) + def test_parse_line_failures(self): with self.assertRaises(ValueError): line_parser.parse_line("mov [10],, [20]") - with self.assertRaises(ValueError): - line_parser.parse_line("mov [10], [[20]]") with self.assertRaises(ValueError): line_parser.parse_line("mov 10 10") diff --git a/planner/asm/program_parser.py b/planner/asm/program_parser.py index e5887b0..5248272 100644 --- a/planner/asm/program_parser.py +++ b/planner/asm/program_parser.py @@ -149,6 +149,8 @@ def parse_data(self, tokens: List[str]): sz = 1 elif tokens[1] == "dw": sz = 2 + elif tokens[1] == "dd": + sz = 4 else: raise ValueError("unsupported data size provided") @@ -157,7 +159,7 @@ def parse_data(self, tokens: List[str]): assert val >= 0 and val < (2**(8*sz)) for _ in range(times): - for byte in val.to_bytes(sz, 'big'): + for byte in val.to_bytes(sz, 'little'): self.add_data(unit.Data(byte)) def parse_bss(self, tokens: List[str]): diff --git a/planner/asm/program_parser_test.py b/planner/asm/program_parser_test.py index 50a8a35..847a58f 100644 --- a/planner/asm/program_parser_test.py +++ b/planner/asm/program_parser_test.py @@ -15,7 +15,7 @@ movc R3, 1 loop: cmp R1, R2 - jeq loop_exit + jz loop_exit add R1, R3 mov [last_value], R1 out 0x00, R1 @@ -26,6 +26,9 @@ section .data last_value db 0 +section .bss +no_use: resb 32 + """.splitlines() class AsmParserTest(TestCase): diff --git a/planner/instruction.py b/planner/instruction.py index 7b2c57e..8f9bd81 100644 --- a/planner/instruction.py +++ b/planner/instruction.py @@ -3,46 +3,101 @@ from planner import unit, util -# # Global parser map to instruction encoder -# _PARSER_MAPPING = {} - -class MBlockSelector(Enum): - '''MBlockSelector defines the purpose of value_s[i].''' - DONT_CARE = -1 - RAM = 0 # value[i] => RAM[value_s[i]] - # TODO: Claim, we don't need AUTO_BRAM - # We should never be reading any data from boot_ram. Any tiny miny data should be - # part of instruction set as CONST. - # AUTO_BRAM = 1 # value[i] => BROM[value_s[i]] is execute_from_brom else RAM - IO = 2 - CONST = 3 - - # TODO: What happens if we try to write on a CONST. +''' +# Stage 0 +load instruction +from instrucition + trim vr_source + trim vrw_source + trim MBlockSelector_stage1 + trim MBlockSelector_stage2 + trim alu_op + trim MBlockSelector_stage3 +# Stage 1 +vr_value = Mblock(vr_source, MBlockSelector_stage1) + +# Stage 2 +vrw_value = RAM[vrw_source OR vr_value as MBlockSelector_stage2] + +# Stage 3 +vw_value = ALU(alu_op, vr_value, vrw_value) + +''' + + +class MBlockSelector_stage1(Enum): + ''' + bits: [is_io][is_const else ram] + ''' + VR_SOURCE_RAM = 0 + VR_SOURCE_IO = 2 + VR_SOURCE_CONST = 3 + # last one for reverse lookup + DONT_CARE = 3 - @staticmethod - def wire(sel, do_not_care = None) -> List: - if sel == MBlockSelector.DONT_CARE: - assert do_not_care is not None - sel = do_not_care - - assert sel in [MBlockSelector.RAM, MBlockSelector.IO, MBlockSelector.CONST], "found: %s" % (sel) + @classmethod + def wire(cls, sel) -> List: + assert isinstance(sel, cls) return [sel.value%2, sel.value>>1] @classmethod def from_binary(cls, bin: List[int]): assert len(bin) == 2 val = bin[0]+bin[1]*2 - assert val >= 0 and val <= 3 - return MBlockSelector(val) + assert val >= 0 and val < 4 + return cls(val) + +class MBlockSelector_stage2(Enum): + ''' + bits: [read_ram(vrw_source) else read_ram(vr_value)] + ''' + VRW_SOURCE_RAM = 0 + VR_VALUE_RAM = 1 + # last one for reverse lookup + DONT_CARE = 0 + + @classmethod + def wire(cls, sel) -> List: + assert isinstance(sel, cls) + return [sel.value%2] + + @classmethod + def from_binary(cls, bin: List[int]): + assert len(bin) == 1 + val = bin[0] + assert val >= 0 and val < 2 + return cls(val) + +class MBlockSelector_stage3(Enum): + NO_WRITE = 0 + VRW_SOURCE_RAM = 1 + VRW_SOURCE_IO = 2 + VRW_VALUE_RAM = 3 + PC_NEXT = 4 + PC_NEXT_IF_ZERO = 5 + + @classmethod + def wire(cls, sel) -> List: + assert isinstance(sel, cls) + return [sel.value%2, (sel.value>>1)%2, sel.value>>2] + + @classmethod + def from_binary(cls, bin: List[int]): + assert len(bin) == 3 + val = bin[0]+bin[1]*2+bin[2]*4 + assert val >= 0 and val < 8 + return cls(val) + class ALU(Enum): ADD = 0 SUB = 1 SHL = 2 SHR = 3 - PASS_R = 4 - AND = 5 - OR = 6 + PASS_R = 4 # vr_value + PASS_RW = 5 # vrw_value + AND = 6 + OR = 7 @staticmethod def wire(sel) -> List: @@ -56,26 +111,30 @@ def from_binary(cls, bits: List[int]): return ALU(value) MAPPING = { - "mblock_selector_r" : [0, 1], - "mblock_selector_rw": [2, 3], - "alu_op" : [4, 5, 6], - "update_pc" : [7], - "mblock_is_write" : [8], + "alu_op" : [0, 1, 2], + "mblock_s1" : [3, 4], + "mblock_s2" : [5], + "mblock_s3" : [6, 7, 8], } class EncodedInstruction: def __init__(self, - mblock_selector_r: MBlockSelector, - mblock_selector_rw: MBlockSelector, - alu_op: ALU, - mblock_is_write: Union[bool,int], - update_program_counter: Union[bool,int]) -> None: - self.mblock_selector_r = mblock_selector_r - self.mblock_selector_rw = mblock_selector_rw + mblock_s1: MBlockSelector_stage1, + mblock_s2: MBlockSelector_stage2, + mblock_s3: MBlockSelector_stage3, + alu_op: ALU) -> None: + self.mblock_s1 = mblock_s1 + self.mblock_s2 = mblock_s2 + self.mblock_s3 = mblock_s3 self.alu_op = alu_op - self.mblock_is_write = 1 if mblock_is_write else 0 - self.update_program_counter = 1 if update_program_counter else 0 + def __eq__(self, __value: object) -> bool: + if not isinstance(__value, EncodedInstruction): + return False + return (self.mblock_s1 == __value.mblock_s1 and + self.mblock_s2 == __value.mblock_s2 and + self.mblock_s3 == __value.mblock_s3 and + self.alu_op == __value.alu_op) @staticmethod def assign_bits(bits: List[int], name: str, values: List[int]): @@ -100,23 +159,23 @@ def encode(self): # bits[16:24] is address_r (address to read only from) # bits[24:32] is address_rw (address to read from or write to) - self.assign_bits(bits, "mblock_selector_r", MBlockSelector.wire(self.mblock_selector_r)) - self.assign_bits(bits, "mblock_selector_rw", MBlockSelector.wire(self.mblock_selector_rw, do_not_care=MBlockSelector.CONST)) - self.assign_bits(bits, "alu_op", ALU.wire(self.alu_op)[0:3]) - self.assign_bits(bits, "update_pc",[self.update_program_counter]) - self.assign_bits(bits, "mblock_is_write",[self.mblock_is_write]) - + self.assign_bits(bits, "mblock_s1", MBlockSelector_stage1.wire(self.mblock_s1)) + self.assign_bits(bits, "mblock_s2", MBlockSelector_stage2.wire(self.mblock_s2)) + self.assign_bits(bits, "mblock_s3", MBlockSelector_stage3.wire(self.mblock_s3)) + self.assign_bits(bits, "alu_op", ALU.wire(self.alu_op)) return sum([bits[i]<0 else 0 for i in range(16)] - mblock_selector_r = MBlockSelector.from_binary(cls.get_bits(encoded_bits, 'mblock_selector_r')) - mblock_selector_rw = MBlockSelector.from_binary(cls.get_bits(encoded_bits, 'mblock_selector_rw')) + mblock_s1 = MBlockSelector_stage1.from_binary(cls.get_bits(encoded_bits, 'mblock_s1')) + mblock_s2 = MBlockSelector_stage2.from_binary(cls.get_bits(encoded_bits, 'mblock_s2')) + mblock_s3 = MBlockSelector_stage3.from_binary(cls.get_bits(encoded_bits, 'mblock_s3')) alu_op = ALU.from_binary(cls.get_bits(encoded_bits, 'alu_op')) - update_program_counter = cls.get_bits(encoded_bits, 'update_pc')[0] - mblock_is_write = cls.get_bits(encoded_bits, 'mblock_is_write')[0] - return EncodedInstruction(mblock_selector_r, mblock_selector_rw, alu_op, mblock_is_write, update_program_counter) + return EncodedInstruction(mblock_s1, mblock_s2, mblock_s3, alu_op) + + def __str__(self): + return str((self.mblock_s1, self.mblock_s2, self.mblock_s3, self.alu_op)) def plug(self, address_rw: unit.LazyLabel, address_r: unit.LazyLabel): return FullyEncodedInstruction(self, address_rw, address_r) @@ -145,6 +204,9 @@ def get_binary(self, ensure_resolved=False) -> List[unit.Data]: unit.Data(self.address_r.get(ensure_resolved=ensure_resolved)) ] + def __str__(self): + return str((str(self.encoded_instruction), str({"rw": self.address_rw, "r": self.address_r}))) + def size(self): return 4 # bytes @@ -202,9 +264,11 @@ def get_str(self, resolved=False, binary=False): printable_values = [] for val_type, val in self.values: - assert val_type in [unit.Operand.ADDRESS, unit.Operand.CONSTANT] + assert val_type in [unit.Operand.ADDRESS, unit.Operand.DADDRESS, unit.Operand.CONSTANT] if val_type == unit.Operand.ADDRESS: printable_values.append("[%s]" % (val.get_str(resolved=resolved))) + elif val_type == unit.Operand.DADDRESS: + printable_values.append("[[%s]]" % (val.get_str(resolved=resolved))) else: printable_values.append("%s" % (val.get_str(resolved=resolved))) return "%s %s" % (self.parser.name, ', '.join(printable_values)) @@ -216,23 +280,87 @@ def size(self): return self.fully_encoded_instruction.size() INSTRUCTIONS = [ - ParserInstruction("IN", unit.Operand.ADDRESS, unit.Operand.CONSTANT, EncodedInstruction(MBlockSelector.IO, MBlockSelector.RAM, ALU.PASS_R, True, False)), - ParserInstruction("OUT", unit.Operand.CONSTANT, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.IO, ALU.PASS_R, True, False)), + ParserInstruction("IN", unit.Operand.ADDRESS, unit.Operand.CONSTANT, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_IO, + MBlockSelector_stage2.DONT_CARE, + MBlockSelector_stage3.VRW_SOURCE_RAM, + ALU.PASS_R)), + ParserInstruction("OUT", unit.Operand.CONSTANT, unit.Operand.ADDRESS, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_RAM, + MBlockSelector_stage2.DONT_CARE, + MBlockSelector_stage3.VRW_SOURCE_IO, + ALU.PASS_R)), # CALL? # HLT? - ParserInstruction("MOV", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.PASS_R, True, False)), - ParserInstruction("MOVC", unit.Operand.ADDRESS, unit.Operand.CONSTANT, EncodedInstruction(MBlockSelector.CONST, MBlockSelector.RAM, ALU.PASS_R, True, False)), - ParserInstruction("ADD", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.ADD, True, False)), - ParserInstruction("SUB", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.SUB, True, False)), - ParserInstruction("SHL", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.SHL, True, False)), - ParserInstruction("SHR", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.SHR, True, False)), - ParserInstruction("AND", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.AND, True, False)), - ParserInstruction("OR", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.OR, True, False)), - - ParserInstruction("CMP", unit.Operand.ADDRESS, unit.Operand.ADDRESS, EncodedInstruction(MBlockSelector.RAM, MBlockSelector.RAM, ALU.SUB, False, False)), - ParserInstruction("JMP", unit.Operand.IGNORE, unit.Operand.CONSTANT, EncodedInstruction(MBlockSelector.CONST, MBlockSelector.DONT_CARE, ALU.PASS_R, False, True)), - # TODO: Fix JEQ will always jump - ParserInstruction("JEQ", unit.Operand.IGNORE, unit.Operand.CONSTANT, EncodedInstruction(MBlockSelector.CONST, MBlockSelector.DONT_CARE, ALU.PASS_R, False, True)) + ParserInstruction("MOV", unit.Operand.ADDRESS, unit.Operand.ADDRESS, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_RAM, + MBlockSelector_stage2.DONT_CARE, + MBlockSelector_stage3.VRW_SOURCE_RAM, + ALU.PASS_R)), + ParserInstruction("MOVC", unit.Operand.ADDRESS, unit.Operand.CONSTANT, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_CONST, + MBlockSelector_stage2.DONT_CARE, + MBlockSelector_stage3.VRW_SOURCE_RAM, + ALU.PASS_R)), + ParserInstruction("LOAD", unit.Operand.ADDRESS, unit.Operand.DADDRESS, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_RAM, + MBlockSelector_stage2.VR_VALUE_RAM, + MBlockSelector_stage3.VRW_SOURCE_RAM, + ALU.PASS_RW)), + ParserInstruction("STORE", unit.Operand.DADDRESS, unit.Operand.ADDRESS, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_RAM, + MBlockSelector_stage2.VRW_SOURCE_RAM, + MBlockSelector_stage3.VRW_VALUE_RAM, + ALU.PASS_R)), + ParserInstruction("CMP", unit.Operand.ADDRESS, unit.Operand.ADDRESS, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_RAM, + MBlockSelector_stage2.VRW_SOURCE_RAM, + MBlockSelector_stage3.NO_WRITE, + ALU.SUB)), + ParserInstruction("CMPC", unit.Operand.ADDRESS, unit.Operand.CONSTANT, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_CONST, + MBlockSelector_stage2.VRW_SOURCE_RAM, + MBlockSelector_stage3.NO_WRITE, + ALU.SUB)), + ParserInstruction("JMP", unit.Operand.IGNORE, unit.Operand.CONSTANT, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_CONST, + MBlockSelector_stage2.DONT_CARE, + MBlockSelector_stage3.PC_NEXT, + ALU.PASS_R)), + # TODO: Ensure flag_alu_zero is updated after stage3. + ParserInstruction("JZ", unit.Operand.IGNORE, unit.Operand.CONSTANT, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_CONST, + MBlockSelector_stage2.DONT_CARE, + MBlockSelector_stage3.PC_NEXT_IF_ZERO, + ALU.PASS_R)) +] + [ + ParserInstruction(ins_name, unit.Operand.ADDRESS, unit.Operand.ADDRESS, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_RAM, + MBlockSelector_stage2.VRW_SOURCE_RAM, + MBlockSelector_stage3.VRW_SOURCE_RAM, + alu_op)) + for ins_name, alu_op in [ + ("ADD", ALU.ADD), + ("SUB", ALU.SUB), + ("SHL", ALU.SHL), + ("SHR", ALU.SHR), + ("AND", ALU.AND), + ("OR", ALU.OR), + ] +] + [ + ParserInstruction(ins_name, unit.Operand.ADDRESS, unit.Operand.CONSTANT, + EncodedInstruction(MBlockSelector_stage1.VR_SOURCE_CONST, + MBlockSelector_stage2.VRW_SOURCE_RAM, + MBlockSelector_stage3.VRW_SOURCE_RAM, + alu_op)) + for ins_name, alu_op in [ + ("ADDC", ALU.ADD), + ("SUBC", ALU.SUB), + ("SHLC", ALU.SHL), + ("SHRC", ALU.SHR), + ("ANDC", ALU.AND), + ("ORC", ALU.OR), + ] ] def get_parser(name: str) -> ParserInstruction: @@ -241,3 +369,11 @@ def get_parser(name: str) -> ParserInstruction: if name == ins.name: return ins raise ValueError(f"Instruction parser for '{name}' not found") + + +def get_parsers_from_encoding(ins: EncodedInstruction) -> ParserInstruction: + ans = [] + for _ins in INSTRUCTIONS: + if _ins.encoded_instruction == ins: + ans.append(_ins) + return ans diff --git a/planner/instruction_test.py b/planner/instruction_test.py index 6d91349..0f9eb4f 100644 --- a/planner/instruction_test.py +++ b/planner/instruction_test.py @@ -7,19 +7,33 @@ class InstructionTest(TestCase): def test_all_instructions_validation(self): all_instructions_sample_list = [ - ("in R1, 20", "IN [1], 20"), - ("out 10, R2", "OUT 10, [2]"), - ("mov R3, R1", "MOV [3], [1]"), - ("movc R4, 0x2", "MOVC [4], 2"), - ("add R5, [35]", "ADD [5], [35]"), - ("sub R6, [0x45]", "SUB [6], [69]"), - ("shl R7, R1", "SHL [7], [1]"), - ("shr R8, R1", "SHR [8], [1]"), - ("and [0x32], R9", "AND [50], [9]"), - ("or [0x12], [0x33]", "OR [18], [51]"), - ("cmp R2, [33]", "CMP [2], [33]"), + ("in R1, 20", "IN [4], 20"), + ("out 10, R2", "OUT 10, [8]"), + ("mov R3, R1", "MOV [12], [4]"), + ("movc R4, 0x2", "MOVC [16], 2"), + ("cmp R2, [33]", "CMP [8], [33]"), + ("cmpc R6, 35", "CMPC [24], 35"), ("jmp 0x55", "JMP 85"), - ("jeq 0x22", "JEQ 34"), + ("jz 0x22", "JZ 34"), + + + ("load R3, [R1]", "LOAD [12], [[4]]"), + ("load R4, [[50]]", "LOAD [16], [[50]]"), + ("store [R1], R3", "STORE [[4]], [12]"), + ("store [[50]], R4", "STORE [[50]], [16]"), + + ("add R0, [10]", "ADD [0], [10]"), + ("addc R1, 10", "ADDC [4], 10"), + ("sub R2, [20]", "SUB [8], [20]"), + ("subc R3, 20", "SUBC [12], 20"), + ("shl R4, [11]", "SHL [16], [11]"), + ("shlc R5, 11", "SHLC [20], 11"), + ("shr R6, [12]", "SHR [24], [12]"), + ("shrc R7, 12", "SHRC [28], 12"), + ("and R8, [50]", "AND [32], [50]"), + ("andc R9, 50", "ANDC [36], 50"), + ("or R0, [65]", "OR [0], [65]"), + ("orc R0, 65", "ORC [0], 65"), ] instructions = set() @@ -27,7 +41,7 @@ def test_all_instructions_validation(self): name, tokens = line_parser.parse_line(input_line) self.assertIsNotNone(name) ins = instruction.get_parser(name).parse(tokens) - self.assertEqual(want, str(ins)) + self.assertEqual(' '.join(want.split()), str(ins)) instructions.add(name) self.assertEqual( diff --git a/planner/sim/bin_parser.py b/planner/sim/bin_parser.py index 74cc857..b8a60e5 100644 --- a/planner/sim/bin_parser.py +++ b/planner/sim/bin_parser.py @@ -8,6 +8,10 @@ PROGRAM_ORG = 0x40 IO_DEVICES = 16 +def binary_array_num(arr: List[int]): + return sum([x<<(8*i) for i, x in enumerate(arr)]) + +FLAGS_BIT_VW_ZERO = 0 class BinRunner: def __init__(self, content): self.ram = [] @@ -17,10 +21,12 @@ def __init__(self, content): self.ram.append(random.randint(0, 256)) self.parse(content) - self.pc = PROGRAM_ORG + self.pc_next = PROGRAM_ORG + self.pc = None # self.is_powered_on = False # self.step() self.is_powered_on = True + self.flags = [0] # self.step() def set_input_device(self, index: int, d: devices.InputDevice): @@ -47,60 +53,83 @@ def read_ram(self, addr: int, count: int) -> List[int]: else: ans.append(self.ram[addr+i]) - logging.info("RAM[%04x]: %s", addr, ans) + logging.info("RAM[%04x] => %s", addr, ans) return ans - @staticmethod - def m_fetch_and_store( - ram: List[int], - input_devices: List[int], - output_devices: List[int], - source: int, - sel: instruction.MBlockSelector, - mblock_is_write: bool, - value: Optional[int] = None, - to_be_ignored_sim_hack: Optional[bool] = False): - if not mblock_is_write: - if sel == instruction.MBlockSelector.RAM: - assert source >= 0 and source < len(ram) - return ram[source] - if sel == instruction.MBlockSelector.CONST: - assert source >= 0 and source < 256 - return source - if sel == instruction.MBlockSelector.IO: - # assert source >= 0 and source < 16 - if to_be_ignored_sim_hack: - value = 0 - else: - # print(f"Input for device[{source}]: ", end="") - value = input_devices[source].take_input() - # value = int(input(), 0) - assert value >= 0 and value < (1<<32) - return value - # input_devices[source] = value - # return input_devices[source] - else: - if sel == instruction.MBlockSelector.RAM: - assert source >= 0 and source < len(ram) - assert value >= 0 and value < 256 - ram[source] = value - return None - if sel == instruction.MBlockSelector.CONST: - # no-op - return - if sel == instruction.MBlockSelector.IO: - # assert source >= 0 and source < 16 - assert value >= 0 and value < (1<<32) - # input_devices[source] = value - output_devices[source].update(value) - return + def write_ram(self, addr: int, count: int, value: int) -> List[int]: + arr_value = [] + for i in range(count): + arr_value.append(value&255) + value>>=8 + + assert addr >= 0 + for i in range(count): + self.ram[(i+addr)%len(self.ram)] = arr_value[i] + + logging.info("RAM[%04x] <= %s", addr, arr_value) + + def m_fetch_and_store_stage1( + self, + input_devices: List[devices.InputDevice], + vr_source: int, + sel: instruction.MBlockSelector_stage1): + assert vr_source >= 0 and vr_source < 256 + if sel == instruction.MBlockSelector_stage1.VR_SOURCE_RAM: + return binary_array_num(self.read_ram(vr_source, 4)) # reading from 8-bit address + if sel == instruction.MBlockSelector_stage1.VR_SOURCE_CONST: + # resize from 1 to 4 bytes + return vr_source + if sel == instruction.MBlockSelector_stage1.VR_SOURCE_IO: + value = input_devices[vr_source].take_input() + assert value >= 0 and value < (1<<32) + return value raise Exception(f"unsupported selector: {sel}") - @staticmethod - def m_alu(rw: int, r: int, op: instruction.ALU): - assert rw>=0 and rw<256 - assert r>=0 and r<256 - MASK = 255 + + def m_fetch_and_store_stage2( + self, + vr_value: int, + vrw_source: int, + sel: instruction.MBlockSelector_stage2): + assert vrw_source >= 0 and vrw_source < 256 + if sel == instruction.MBlockSelector_stage2.VR_VALUE_RAM: + return binary_array_num(self.read_ram(vr_value, 4)) # reading from 32-bit address + if sel == instruction.MBlockSelector_stage2.VRW_SOURCE_RAM: + return binary_array_num(self.read_ram(vrw_source, 4)) # reading from 8-bit address + raise Exception(f"unsupported selector: {sel}") + + def m_fetch_and_store_stage3( + self, + output_devices: List[devices.Device], + vw_value: int, + vrw_value: int, + vrw_source: int, + sel: instruction.MBlockSelector_stage3): + assert vrw_source >= 0 and vrw_source < 256 + if sel == instruction.MBlockSelector_stage3.NO_WRITE: + return + if sel == instruction.MBlockSelector_stage3.VRW_SOURCE_RAM: + return self.write_ram(vrw_source, 4, vw_value) # write using 8-bit address + if sel == instruction.MBlockSelector_stage3.VRW_VALUE_RAM: + return self.write_ram(vrw_value, 4, vw_value) # write using 8-bit address + if sel == instruction.MBlockSelector_stage3.VRW_SOURCE_IO: + assert vw_value >= 0 and vw_value < (1<<32) + output_devices[vrw_source].update(vw_value) + return + if sel == instruction.MBlockSelector_stage3.PC_NEXT: + self.pc_next = vw_value + return + if sel == instruction.MBlockSelector_stage3.PC_NEXT_IF_ZERO: + # check previous vw_value flags + if self.flags[FLAGS_BIT_VW_ZERO] == 1: + self.pc_next = vw_value + return + raise Exception(f"unsupported selector: {sel}") + + def m_alu(self, rw: int, r: int, op: instruction.ALU): + assert rw>=0 and rw<(1<<32) + assert r>=0 and r<(1<<32) + MASK = ((1<<32)-1) if op == instruction.ALU.ADD: return MASK&(rw+r) if op == instruction.ALU.SUB: @@ -112,6 +141,8 @@ def m_alu(rw: int, r: int, op: instruction.ALU): return MASK&(rw>>r) if op == instruction.ALU.PASS_R: return MASK&(r) + if op == instruction.ALU.PASS_RW: + return MASK&(rw) if op == instruction.ALU.AND: return MASK&(rw&r) if op == instruction.ALU.OR: @@ -128,53 +159,39 @@ def m_pc_next(pc: int, value: int, flag_alu_zero: bool, update_program_counter: return pc+4 def step(self): + self.pc = self.pc_next + self.pc_next = self.pc + 4 + logging.info("PC: 0x%x, flags: %s", self.pc, self.flags) ins_binary = self.read_ram(self.pc, 4) ins = instruction.FullyEncodedInstruction.from_binary(ins_binary) - mblock_selector_r = ins.encoded_instruction.mblock_selector_r - mblock_selector_rw = ins.encoded_instruction.mblock_selector_rw + logging.info("Instruction data: %s", ins) + logging.info("Instruction encoding: %s", + [str(x) for x in instruction.get_parsers_from_encoding(ins.encoded_instruction)]) + mblock_s1 = ins.encoded_instruction.mblock_s1 + mblock_s2 = ins.encoded_instruction.mblock_s2 + mblock_s3 = ins.encoded_instruction.mblock_s3 alu_op = ins.encoded_instruction.alu_op - mblock_is_write = ins.encoded_instruction.mblock_is_write - update_program_counter = ins.encoded_instruction.update_program_counter vr_source = ins.address_r.get() vrw_source = ins.address_rw.get() - value_r = self.m_fetch_and_store( - self.ram, + vr_value = self.m_fetch_and_store_stage1( self.input_devices, - self.output_devices, vr_source, - mblock_selector_r, - False, - value=None - ) - value_rw = self.m_fetch_and_store( - self.ram, - self.input_devices, - self.output_devices, + mblock_s1) + vrw_value = self.m_fetch_and_store_stage2( + vr_value, vrw_source, - mblock_selector_rw, - False, - value=None, - to_be_ignored_sim_hack = (alu_op == instruction.ALU.PASS_R) - ) + mblock_s2) - value = self.m_alu(value_rw, value_r, alu_op) - flag_alu_zero = (value == 0) + vw_value = self.m_alu(vrw_value, vr_value, alu_op) - self.m_fetch_and_store( - self.ram, - self.input_devices, + self.m_fetch_and_store_stage3( self.output_devices, + vw_value, + vrw_value, vrw_source, - mblock_selector_rw, - mblock_is_write and self.is_powered_on, - value=value - ) - - self.pc = self.m_pc_next( - self.pc, - value, - flag_alu_zero, - update_program_counter, - self.is_powered_on) + mblock_s3) + + self.flags[FLAGS_BIT_VW_ZERO] = 1 if (vw_value==0) else 0 + diff --git a/planner/sim/bin_parser_test.py b/planner/sim/bin_parser_test.py index 30145f4..23108a9 100644 --- a/planner/sim/bin_parser_test.py +++ b/planner/sim/bin_parser_test.py @@ -8,16 +8,41 @@ # Sample Program PROGRAM_ORG equ 0x40 -ADD_WITH equ 15 section .text main: - movc R2, ADD_WITH + movc R1, 0 + + # add array0+array1 to R1 + movc R2, array0 + load R3, [R2] + add R1, R3 + addc R2, 4 + load R3, [R2] + add R1, R3 + + movc R2, 15 + # input(0x05)*15 in R4, 0x05 - add R4, R2 - out 0x06, R4 +loop_start: + cmpc R2, 0 + jz loop_end + subc R2, 1 + add R1, R4 + jmp loop_start +loop_end: + # answer is in R1 + movc R2, 12 # memory address of R3 + store [R2], R1 + out 0x06, R3 loop_exit: jmp loop_exit + +section .data +array0 dd 31 +array1 dd 24 + + """.splitlines() class BinParserTest(TestCase): @@ -29,8 +54,8 @@ def test_overall(self): binary_program = asm.get_str(resolved=True, rom_binary=True) _bin = bin_parser.BinRunner(binary_program) - fake_input = devices.LatchInput("fake") - fake_ouput = devices.Device() + fake_input = devices.LatchInput("fake", bits=32) + fake_ouput = devices.Device(bits=32) _bin.set_input_device(5, fake_input) _bin.set_output_device(6, fake_ouput) @@ -38,4 +63,4 @@ def test_overall(self): fake_input.set_input(56) for _ in range(100): _bin.step() - self.assertEqual(fake_ouput.get(), 56+15) + self.assertEqual(fake_ouput.get(), 31+24+56*15) diff --git a/planner/sim/devices.py b/planner/sim/devices.py index 9d8130a..b8b570c 100644 --- a/planner/sim/devices.py +++ b/planner/sim/devices.py @@ -1,5 +1,5 @@ from typing import List -import random +import logging class Device: @@ -53,8 +53,8 @@ def take_input(self): class LatchInput(InputDevice): - def __init__(self, name: str): - super(LatchInput, self).__init__() + def __init__(self, name: str, **kwargs): + super(LatchInput, self).__init__(**kwargs) self.name = name self.update(0) diff --git a/planner/unit.py b/planner/unit.py index e9cb154..d8190cf 100644 --- a/planner/unit.py +++ b/planner/unit.py @@ -55,6 +55,8 @@ class Operand(Enum): ADDRESS = 1 CONSTANT = 2 IGNORE = 3 + # Address of address, equivalent of pointers + DADDRESS = 4 def __repr__(self): return self.name diff --git a/programs/boot_sequence.asm b/programs/boot_sequence.asm new file mode 100644 index 0000000..308d482 --- /dev/null +++ b/programs/boot_sequence.asm @@ -0,0 +1,38 @@ +# TODO: This is not ready yet +# Program +# ROM[BootSequence] +# +# Input devices required +# * ROM[Program] output at 0 +# Output devices +# * ROM[Program] address-line at 0 +# Really small program to copy ROM[Program] to RAM[Program] + +PROGRAM_ORG equ 0x40 + +section .text + main: + # read metadata: program size + movc R9, 0 # const -> ram + out 0, R9 # ram -> io + in R0, 0 # io -> ram + movc R8, 1 + + movc R2, PROGRAM_ORG # const -> ram + movc R1, 1 # const -> ram + _copy_more: + out 0, R1 # ram -> io + in R3, 0 # io -> ram + add R1, R8 + add R2, R8 + store [R2], R3 # ram -> ram + # bytes left to copy + subc R0, 1 + cmp R0, R9 + jz _copy_completed + jmp _copy_more + + _copy_completed: + jmp _copy_completed + + diff --git a/programs/boot_sequence.not_ready_asm b/programs/boot_sequence.not_ready_asm deleted file mode 100644 index f1e74fc..0000000 --- a/programs/boot_sequence.not_ready_asm +++ /dev/null @@ -1,34 +0,0 @@ -# Program -# ROM[BootSequence] -# -# Input devices required -# * ROM[Program] output at 0 -# Output devices -# * ROM[Program] address-line at 0 -# Really small program to copy ROM[Program] to RAM[Program] - -PROGRAM_START equ 0x20 - -section .text - main: - # read metadata: program size - mov [R1], 0 # const -> ram - OUT 0, [R1] # ram -> io - IN [R0], 0 # io -> ram - - mov [R2], PROGRAM_START # const -> ram - mov [R1], 1 # const -> ram - _copy_more: - OUT 0, [R1] # ram -> io - IN [R2], 0 # io -> ram - add [R2], 1 - add [R1], 1 - # bytes left to copy - sub [R0], 1 - cmp [R0], 0 - jneq _copy_more - - _copy_completed: - jmp _copy_completed - -