ソースを参照

Memory update

Finalised OISC memory modifications, rewritten memory generation and
assembly compilation. Now assembly compled files are saved in binary .o
file, and then converted to needed format using tools/format_utils.py
script.

OISC memory is sliced to 3 parts and stored in 9x1024 memory blocks
(each instruction 13 bits, 3 parts store 2 instructions and parity bit).

Added various probes to processor.
Min 5 年 前
コミット
9fca4d2aa7
11 ファイル変更509 行追加262 行削除
  1. 36 38
      Makefile
  2. 10 4
      scripts/update_oisc8.tcl
  3. 8 0
      src/blocks/debug.sv
  4. 13 13
      src/blocks/rom.sv
  5. 3 0
      src/const.sv
  6. 10 1
      src/oisc/cpu.sv
  7. 7 7
      src/oisc/romblock.sv
  8. 3 3
      src/top.sv
  9. 127 72
      tools/asm_compiler.py
  10. 161 0
      tools/format_utils.py
  11. 131 124
      tools/oisc8asm.py

+ 36 - 38
Makefile

@@ -1,6 +1,6 @@
 
 # Configuration
-PROCESSOR ?= RISC8  # Also supported OISC8
+PROCESSOR ?= NONE  # Supported RISC8, OISC8
 PROCESSOR_LOW = $(strip $(shell echo $(PROCESSOR) | tr A-Z a-z))
 
 QUARTUS_DIR = /opt/altera/18.1/quartus
@@ -14,6 +14,9 @@ QUARTUS_MACROS =  --set VERILOG_MACRO="SYNTHESIS=1"
 OUTPUTP = output_files/$(PROJECT_NAME)
 OUT_ASM = $(OUTPUTP).sof
 
+# assembly compiled and memory sliced files
+BUILD_DIR = memory/build
+
 # Program & Monitor
 JTAG ?= 1
 TTY  ?= /dev/ttyUSB0
@@ -21,30 +24,29 @@ BAUD ?= 9600
 
 GENTABLE_BIN = python3 tools/gen_sv.py
 ASMC = python3 tools/$(PROCESSOR_LOW)asm.py
+FUTILS = python3 tools/format_utils.py
 
-MEMSIZE ?= 4096
-RAMSIZE ?= -1
-MEMDEP := $(shell find memory -name '*${PROCESSOR_LOW}.asm')
-MEMSLICES = 0 1 2 3
+RAM_SIZE ?= 4096
+RAM_WIDTH ?= 16
 
+ASMDEP := $(shell find memory -name '*${PROCESSOR_LOW}.asm')
 ifeq "${PROCESSOR_LOW}" "risc8"
-MEMRES = $(foreach i,$(MEMSLICES),$(MEMDEP:.asm=.text_$(i).mem)) \
-		$(foreach i,$(MEMSLICES),$(MEMDEP:.asm=.text_$(i).mif)) \
-		$(foreach i,$(MEMSLICES),$(MEMDEP:.asm=.text_$(i).uhex)) \
-		$(MEMDEP:.asm=.data.mem) \
-		$(MEMDEP:.asm=.data.uhex) \
-		$(MEMDEP:.asm=.data.mif)
+MEMSLICES = 0 1 2 3
+MEMTYPE = mem
+TEXT_WIDTH = 8
 else ifeq "${PROCESSOR_LOW}" "oisc8"
-MEMRES = $(MEMDEP:.asm=.text.mem)  \
-		 $(MEMDEP:.asm=.text.uhex) \
-		 $(MEMDEP:.asm=.text.mif)  \
-		 $(MEMDEP:.asm=.data.mem)  \
-		 $(MEMDEP:.asm=.data.uhex) \
-		 $(MEMDEP:.asm=.data.mif)
+MEMSLICES = 0 1 2
+MEMTYPE = binary
+TEXT_WIDTH = 9
 else
 $(error "Processor not supported: ${PROCESSOR_LOW}")
 endif
 
+BUILD_OUT = $(addprefix ${BUILD_DIR}/,$(notdir $(ASMDEP:.asm=.text.o) $(ASMDEP:.asm=.data.o)))
+
+MEM_BUILD =	$(addprefix ${BUILD_DIR}/,$(notdir $(ASMDEP:.asm=.data.mem) $(ASMDEP:.asm=.data.mif) $(foreach i,$(MEMSLICES),$(ASMDEP:.asm=.text.$(i).mem)) $(foreach i,$(MEMSLICES),$(ASMDEP:.asm=.text.$(i).mif)) ) )
+
+#$(error MEM_BUILD: ${MEM_BUILD})
 VERILOG ?= $(wildcard src/*/*.sv) 
 
 # Genreate sv case table from csv
@@ -53,10 +55,10 @@ define execute-gentable
 $(GENTABLE_BIN) $(1) $(1:.csv=.sv)
 endef
 
-analysis: compile
+analysis: $(MEM_BUILD)
 	${QUARTUS_DIR}/bin/quartus_map --read_settings_files=on --write_settings_files=off ${QUARTUS_MACROS} ${PROJECT_NAME} -c ${PROJECT_NAME} --analysis_and_elaboration
 
-$(OUT_ASM): $(MEMDEP)
+$(OUT_ASM): $(ASMDEP)
 	${QUARTUS_DIR}/bin/quartus_map --read_settings_files=on --write_settings_files=off ${QUARTUS_MACROS} ${PROJECT_NAME} -c ${PROJECT_NAME} 
 	${QUARTUS_DIR}/bin/quartus_fit --read_settings_files=off --write_settings_files=off ${QUARTUS_MACROS} ${PROJECT_NAME} -c ${PROJECT_NAME} 
 	${QUARTUS_DIR}/bin/quartus_asm --read_settings_files=off --write_settings_files=off ${QUARTUS_MACROS} ${PROJECT_NAME} -c ${PROJECT_NAME} 
@@ -100,36 +102,32 @@ simulate: $(VERILOG)
 testbench: compile
 	${MODELSIM_BIN} -c -do "vsim work.$(basename $(notdir $(VERILOG)))_tb" -do "run -all" -do exit
 
-compile: $(MEMRES)
-
-%.text_0.mem %.text_1.mem %.text_2.mem %.text_3.mem: %.asm
-	$(ASMC) -t mem -f $< -S $(words $(MEMSLICES)) .text
+$(BUILD_DIR)/%.text.o $(BUILD_DIR)/%.data.o: $(ASMDEP)
+	$(ASMC) $< -o $(BUILD_DIR) -f 
 
-%.text_0.mif %.text_1.mif %.text_2.mif %.text_3.mif: %.asm
-	$(ASMC) -t mif -f $< -S $(words $(MEMSLICES)) .text
+build: $(BUILD_OUT)
 
-%.text_0.uhex %.text_1.uhex %.text_2.uhex %.text_3.uhex: %.asm
-	$(ASMC) -t uhex -f $< -S $(words $(MEMSLICES)) .text
+%.text.0.mem %.text.1.mem %.text.2.mem %.text.3.mem: %.text.o $(BUILD_OUT)
+	$(FUTILS) -w $(TEXT_WIDTH) -t memb -f -S $(words $(MEMSLICES)) $<
 
-%.data.mem: %.asm
-	$(ASMC) -t mem -f $< .data
+%.text.0.mif %.text.1.mif %.text.2.mif %.text.3.mif: %.text.o $(BUILD_OUT)
+	$(FUTILS) -w $(TEXT_WIDTH) -t mif -f -S $(words $(MEMSLICES)) $<
 
-%.data.mif: %.asm
-	$(ASMC) -t mif -f $< .data
+%.data.mem: %.data.o $(BUILD_OUT)
+	$(FUTILS) -w $(RAM_WIDTH) -t memh -f $<
 
-%.data.uhex: %.asm
-	$(ASMC) -t uhex -f $< .data
+%.data.mif: %.data.o $(BUILD_OUT)
+	$(FUTILS) -w $(RAM_WIDTH) -t mif -f $<
 
-%.text.mem: %.asm
+%.text.mem: %.text.o $(BUILD_OUT)
 	$(ASMC) -t mem -f $< .text
 
-%.text.mif: %.asm
+%.text.mif: %.text.o $(BUILD_OUT)
 	$(ASMC) -t mif -f $< .text
 
-%.text.uhex: %.asm
-	$(ASMC) -t uhex -f $< .text
+memory: $(MEM_BUID)
 
-flash: $(MEMRES)
+flash: $(BUILD_OUT)
 	$(QUARTUS_DIR)/bin/quartus_stp -t ./scripts/update_$(PROCESSOR_LOW).tcl
 
 clean:

+ 10 - 4
scripts/update_oisc8.tcl

@@ -17,12 +17,18 @@ start_insystem_source_probe -hardware_name $hardware_name -device_name $device_n
 write_source_data -instance_index 0 -value 1 -value_in_hex
 
 puts "Flashing ram...";
-set content [exec cat ./memory/oisc8.data.uhex]
+set content [exec python ./tools/format_utils.py -w 16 -t uhex -s ./memory/build/oisc8.data.o]
 write_content_to_memory -instance_index 0 -content $content -content_in_hex -start_address 0 -word_count 4096
 
-puts "Flashing rom...";
-set content [exec cat ./memory/oisc8.text.uhex]
-write_content_to_memory -instance_index 1 -content $content -content_in_hex -start_address 0 -word_count 1024
+puts "Flashing rom0...";
+set content [exec python ./tools/format_utils.py -w 9 -S 3 -n 0 -t ubin -s ./memory/build/oisc8.text.o]
+write_content_to_memory -instance_index 1 -content $content -start_address 0 -word_count 1024
+puts "Flashing rom1...";
+set content [exec python ./tools/format_utils.py -w 9 -S 3 -n 1 -t ubin -s ./memory/build/oisc8.text.o]
+write_content_to_memory -instance_index 2 -content $content -start_address 0 -word_count 1024
+puts "Flashing rom2...";
+set content [exec python ./tools/format_utils.py -w 9 -S 3 -n 2 -t ubin -s ./memory/build/oisc8.text.o]
+write_content_to_memory -instance_index 3 -content $content -start_address 0 -word_count 1024
 
 write_source_data -instance_index 0 -value 0 -value_in_hex
 end_insystem_source_probe

+ 8 - 0
src/blocks/debug.sv

@@ -62,6 +62,7 @@ module sys_comb(source, probe);
 	input wire[WIDTH-1:0] probe;
 	output wire[WIDTH-1:0] source;
 
+	`ifdef SYNTHESIS
 	altsource_probe_top #(
 		.sld_auto_instance_index ("YES"),
 		.sld_instance_index      (0),
@@ -74,6 +75,7 @@ module sys_comb(source, probe);
 		.source(source),      // sources.source
 		.source_ena ('d0)    // (terminated)
 	);
+	`endif
 endmodule
 
 
@@ -82,6 +84,7 @@ module sys_ss (source);
 	parameter WIDTH = 1;
 	output wire[WIDTH-1:0] source;
 
+	`ifdef SYNTHESIS
 	altsource_probe_top #(
 		.sld_auto_instance_index ("YES"),
 		.sld_instance_index      (0),
@@ -94,6 +97,9 @@ module sys_ss (source);
 		.source     (source), // sources.source
 		.source_ena (1'b1)    // (terminated)
 	);
+	`else
+	assign source = 'd0;
+	`endif
 
 endmodule
 
@@ -102,6 +108,7 @@ module sys_sp (probe);
 	parameter WIDTH = 1;
 	input wire[WIDTH-1:0] probe;
 
+	`ifdef SYNTHESIS
 	altsource_probe_top #(
 		.sld_auto_instance_index ("YES"),
 		.sld_instance_index      (0),
@@ -112,5 +119,6 @@ module sys_sp (probe);
 	) in_system_sources_probes_0 (
 		.probe (probe)  // probes.probe
 	);
+	`endif
 
 endmodule

+ 13 - 13
src/blocks/rom.sv

@@ -85,14 +85,14 @@ module pseudo_rom(addr, clk, q);
 	input  wire [AWIDTH-1:0] addr;
 	output reg [WIDTH-1:0] q;
 	
-	initial $display("Initialising ROM Memory: %s", PROGRAM);
+	initial begin
+		$display("Initialising ROM Memory: %s", PROGRAM);
+		if (BINARY==1) $readmemb(PROGRAM, rom);
+		else $readmemh(PROGRAM, rom);
+	end
 	
 	reg [AWIDTH-1:0] addr0;
 	logic [WIDTH-1:0] rom [NUMWORDS:0];
-	initial begin
-		if(BINARY==0) $readmemh(PROGRAM, rom);
-		else $readmemb(PROGRAM, rom);
-	end
 	always_ff@(posedge clk) addr0 <= addr; 	
 	assign q = rom[addr0];
 
@@ -136,16 +136,16 @@ module rom (
 	end
 	//always_ff@(posedge clock) q <= qn;
 
+	m9k_rom#({PROGRAM, "_0.mif"}, "rom0") rom0(addr0, clock, q0);
+	m9k_rom#({PROGRAM, "_1.mif"}, "rom1") rom1(addr1, clock, q1);
+	m9k_rom#({PROGRAM, "_2.mif"}, "rom2") rom2(addr2, clock, q2);
+	m9k_rom#({PROGRAM, "_3.mif"}, "rom3") rom3(addr3, clock, q3);
 	`ifdef SYNTHESIS
-		m9k_rom#({PROGRAM, "_0.mif"}, "rom0") rom0(addr0, clock, q0);
-		m9k_rom#({PROGRAM, "_1.mif"}, "rom1") rom1(addr1, clock, q1);
-		m9k_rom#({PROGRAM, "_2.mif"}, "rom2") rom2(addr2, clock, q2);
-		m9k_rom#({PROGRAM, "_3.mif"}, "rom3") rom3(addr3, clock, q3);
 	`else
-		pseudo_rom#({PROGRAM, "_0.mem"}) rom0(addr0, clock, q0);
-		pseudo_rom#({PROGRAM, "_1.mem"}) rom1(addr1, clock, q1);
-		pseudo_rom#({PROGRAM, "_2.mem"}) rom2(addr2, clock, q2);
-		pseudo_rom#({PROGRAM, "_3.mem"}) rom3(addr3, clock, q3);
+		//pseudo_rom#({PROGRAM, "_0"}) rom0(addr0, clock, q0);
+		//pseudo_rom#({PROGRAM, "_1"}) rom1(addr1, clock, q1);
+		//pseudo_rom#({PROGRAM, "_2"}) rom2(addr2, clock, q2);
+		//pseudo_rom#({PROGRAM, "_3"}) rom3(addr3, clock, q3);
 		// Currently read address (for debugging)
 		reg [11:0] ff_addr;
 		always_ff@(posedge clock) ff_addr <= address;

+ 3 - 0
src/const.sv

@@ -6,3 +6,6 @@
 
 // Add debugging hardware to processor
 `define DEBUG
+
+`define ROMDIR "../../memory/build/"
+`define RAMDIR "../../memory/build/"

+ 10 - 1
src/oisc/cpu.sv

@@ -30,7 +30,7 @@ module oisc8_cpu(processor_port port);
 	sys_sp#("REG1", `DWIDTH) sys_reg1(reg1);
 	`endif
 
-	pc_block#(.PROGRAM("../../memory/oisc8.text")) pc0(bus0.port, bus0.iport);
+	pc_block#(.PROGRAM({`ROMDIR, "oisc8.text"})) pc0(bus0.port, bus0.iport);
 	alu_block alu0(bus0.port);
 	mem_block ram0(bus0.port, port);
 	oisc_com_block com0(bus0.port, port);
@@ -85,6 +85,11 @@ module mem_block(IBus.port bus, processor_port port);
 			.wr(w2)
 	);
 	
+	`ifdef DEBUG
+	sys_sp#("MEMP", 24) sys_memp(pointer);
+	sys_sp#("MEMC", 16) sys_memc(cached);
+	`endif
+	
 	PortInput#(.ADDR(MEMSWLO)) p_mem0sw(.bus(bus),.data_from_bus(data[7:0]),.wr(wd0));	
 	PortInput#(.ADDR(MEMSWHI)) p_mem1sw(.bus(bus),.data_from_bus(data[15:8]),.wr(wd1));
     
@@ -114,6 +119,10 @@ module mem_block(IBus.port bus, processor_port port);
 			if(st_push_en|st_pop_en) stp <= stpp;
 		end
 	end
+	
+	`ifdef DEBUG
+	sys_sp#("STP", 16) sys_stp(stp);
+	`endif
 
 	PortInput#(.ADDR(STACK)) p_push(
 		.bus(bus),

+ 7 - 7
src/oisc/romblock.sv

@@ -32,23 +32,23 @@ module pc_block(IBus.port bus, IBus.iport port);
 	wire [26:0] instrBlock;
 	wire [12:0] instrA, instrB;
 	`ifdef SYNTHESIS
-	m9k_rom#(.PROGRAM({PROGRAM, "_0.mif"}),.NAME("rom0"),.WIDTH(9),.NUMWORDS(1024))
+	m9k_rom#(.PROGRAM({PROGRAM, ".0.mif"}),.NAME("rom0"),.WIDTH(9),.NUMWORDS(1024))
 		rom0(pc[10:1], bus.clk, instrBlock[26:18]);
-	m9k_rom#(.PROGRAM({PROGRAM, "_1.mif"}),.NAME("rom1"),.WIDTH(9),.NUMWORDS(1024))
+	m9k_rom#(.PROGRAM({PROGRAM, ".1.mif"}),.NAME("rom1"),.WIDTH(9),.NUMWORDS(1024))
 		rom1(pc[10:1], bus.clk, instrBlock[17:9]);
-	m9k_rom#(.PROGRAM({PROGRAM, "_2.mif"}),.NAME("rom2"),.WIDTH(9),.NUMWORDS(1024))
+	m9k_rom#(.PROGRAM({PROGRAM, ".2.mif"}),.NAME("rom2"),.WIDTH(9),.NUMWORDS(1024))
 		rom2(pc[10:1], bus.clk, instrBlock[8:0]);
 	`else
-	pseudo_rom#(.PROGRAM({PROGRAM, "_0.mem"}),.WIDTH(9),.NUMWORDS(1024),.BINARY(1)) 
+	pseudo_rom#(.PROGRAM({PROGRAM, ".0.mem"}),.WIDTH(9),.NUMWORDS(1024),.BINARY(1)) 
 		rom0(pc[10:1], bus.clk, instrBlock[26:18]);
-	pseudo_rom#(.PROGRAM({PROGRAM, "_1.mem"}),.WIDTH(9),.NUMWORDS(1024),.BINARY(1)) 
+	pseudo_rom#(.PROGRAM({PROGRAM, ".1.mem"}),.WIDTH(9),.NUMWORDS(1024),.BINARY(1)) 
 		rom1(pc[10:1], bus.clk, instrBlock[17:9]);
-	pseudo_rom#(.PROGRAM({PROGRAM, "_2.mem"}),.WIDTH(9),.NUMWORDS(1024),.BINARY(1)) 
+	pseudo_rom#(.PROGRAM({PROGRAM, ".2.mem"}),.WIDTH(9),.NUMWORDS(1024),.BINARY(1)) 
 		rom2(pc[10:1], bus.clk, instrBlock[8:0]);
 	`endif
 	assign instrA = instrBlock[26:14];
 	assign instrB = instrBlock[13:1];
-	assign instr = pc0 ? instrA : instrB;
+	assign instr = pc0==1'b0 ? instrA : instrB;  // weird behaviour with euality
 
 	`ifdef DEBUG
 	reg [15:0] pcp;  // Current program counter for debugging

+ 3 - 3
src/top.sv

@@ -91,9 +91,9 @@ module top(
 	wire  		ram_rd_ack;
 	
 	`ifdef OISC
-	ram#("../../memory/oisc8.data") 
+	ram#({`RAMDIR, "oisc8.data"}) 
 	`elsif
-	ram#("../../memory/risc8.data") 
+	ram#({`RAMDIR, "risc8.data"}) 
 	`endif
 	ram_block0(ram_addr[11:0], mclk, ram_wr_data, ram_wr_en, ram_rd_en, ram_rd_data);
 	
@@ -255,7 +255,7 @@ module top_tb;
 			//KEY[1] = 0;
 			//#5us;
 			//KEY[1] = 1;
-			#300us;
+			#10us;
 			$stop;
 	end
 	initial forever #10ns CLK50 = ~CLK50;

+ 127 - 72
tools/asm_compiler.py

@@ -103,8 +103,21 @@ class Section:
         self.data = b''
         self.count = 0
         self.width = 1
-        self.length = 1
-        self.size = 2 ** 8
+        self.length = 3
+        self.options = {}
+        self.depth = 2 ** 8
+
+    @property
+    def bin_width(self):
+        if 'bin_width' in self.options and self.options['bin_width'].isdecimal():
+            return int(self.options['bin_width'])
+        return self.width * 8
+
+    @property
+    def fill_bits(self):
+        if 'fill_bits' in self.options and self.options['fill_bits'].isdecimal():
+            return int(self.options['fill_bits'])
+        return self.depth * self.width * 8
 
 
 class Compiler:
@@ -340,7 +353,7 @@ class Compiler:
                 for i, mline0 in enumerate(mline):
                     for j in range(len(argsp)):
                         mline0 = mline0.replace(f'%{j + 1}', argsp[j])
-                    mline[i] = re.sub(r'(%{2})([\w$#~.?]+)', mlabel+r'.\2', mline0)
+                    mline[i] = re.sub(r'(%{2})([\w$#~.?]+)', mlabel + r'.\2', mline0)
                 try:
                     scope = self.__code_compiler(file, lnum, mline, csect, scope, True)
                 except CompilingError as e:
@@ -412,15 +425,29 @@ class Compiler:
                     section_name = line_args[1].lower()
                     if section_name not in sections:
                         s = Section()
+                        options = {}
                         if len(line_args) == 3:
-                            m = secs_re.match(line_args[2])
-                            if m is not None:
-                                g = m.groups()
-                                s.width = int(g[0])
-                                s.length = int(g[1])
-                                s.size = int(g[2])
-                            else:
-                                raise CompilingError(f"Invalid section argument: {line_args[2]}")
+                            for sp in line_args[2].split(','):
+                                if '=' not in sp:
+                                    continue
+                                sp2 = sp.split('=', 1)
+                                key = sp2[0].lower()
+                                val = sp2[1]
+                                s.options[key] = val
+                                if not val.isdecimal():
+                                    continue
+                                if key == 'depth':
+                                    s.depth = int(val)
+                                if key == 'width':
+                                    s.width = int(val)
+                            # m = secs_re.match(line_args[2])
+                            # if m is not None:
+                            #     g = m.groups()
+                            #     s.width = int(g[0])
+                            #     s.length = int(g[1])
+                            #     s.size = int(g[2])
+                            # else:
+                            #     raise CompilingError(f"Invalid section argument: {line_args[2]}")
                         sections[section_name] = s
                     csect = sections[section_name]
                     continue
@@ -495,7 +522,8 @@ class Compiler:
                     print(f"ERROR {file}:{lnum}: {e.message}")
         if failure:
             return None
-        return {k: (v.width, v.length, v.size, v.data) for k, v in sections.items()}
+        # return {k: (v.width, v.length, v.size, v.data) for k, v in sections.items()}
+        return sections
 
     def decompile(self, binary):
         addr = 0
@@ -537,65 +565,92 @@ class Compiler:
         return '\n'.join(res)
 
 
-def convert_to_binary(data, bit_width=8):
-    bin_data = ''.join([format(i, '08b') for i in data])
-    a = '\n'.join(bin_data[i*bit_width:i*bit_width+bit_width] for i in range(0, len(data)//bit_width))
-    return a.encode()
-
-
-def convert_to_mem(data, width=1, uhex=False, reverse=False):
-    x = b''
+def main(asmc):
+    import sys
+    import argparse
+    from os import path, mkdir
+    from bitstring import BitArray
+
+    parser = argparse.ArgumentParser(description='Assembly compiler', add_help=True)
+    parser.add_argument('file', help='Files to compile')
+    parser.add_argument('-o', '--output', help='Output directory')
+    parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
+    parser.add_argument('-s', '--stdout', action='store_true', help='Print to stdout')
+    parser.add_argument('-D', '--decompile', action='store_true', help='Print decompiled')
+    args = parser.parse_args(sys.argv[1:])
+    bname = path.basename(args.file).rsplit('.', 1)[0]
+
+    if not path.isfile(args.file):
+        print(f'No file {args.file}!')
+        sys.exit(1)
+
+    output_dir = args.output or path.dirname(args.file)
+    if not path.exists(output_dir):
+        mkdir(output_dir)
+
+    data = asmc.compile_file(args.file)
+    if data is not None:
+        for sec_name in data:
+            sec = data[sec_name]
+            if sec_name == '.text' and args.decompile:
+                print(asmc.decompile(sec.data))
+
+            output = path.join(output_dir, f'{bname}.{sec_name.strip(".")}.o')
+            if not args.stdout and not args.force:
+                if path.isfile(output):
+                    print(f'Output file already exists {output}!')
+                    inval = ''
+                    while True:
+                        inval = input('Override? [y/n]: ')
+                        inval = inval.lower().strip()
+                        if inval != 'y' and inval != 'n':
+                            print('Please type y or n')
+                            continue
+                        break
+                    if inval == 'n':
+                        continue
 
-    if uhex:
-        if width == 2:
-            for i in range(int(len(data) / 2)):
-                x += format(data[-(i * 2) - 2], f'02x').upper().encode()
-                x += format(data[-(i * 2) - 1], f'02x').upper().encode()
-        else:
-            for i in range(len(data)):
-                if reverse:
-                    x += format(data[i], f'02x').upper().encode()
-                else:
-                    x += format(data[-i - 1], f'02x').upper().encode()
-        return x
-
-    if width == 2:
-        datax = [(x << 8) | y for x, y in zip(data[0::2], data[1::2])]
-        if len(data) % 2 == 1:
-            datax.append(data[-1] << 8)
+            parity = 0
+            if 'parity' in sec.options and sec.options['parity'].isdigit():
+                parity = int(sec.options['parity'])
+
+            content = sec.data
+
+            used = -1
+            # if fill_bits == 0 and len(sec.data) < sec.depth:
+            #     used = len(content)/sec.depth
+            #     content += (sec.depth*sec.width - len(content)) * bytearray(b'\x00')
+
+            # converting to BitArray
+            binary = BitArray()
+            for i in range(sec.depth):
+                if len(binary) >= sec.fill_bits:  # FIXME: better solution
+                    break
+                cell = content[i*sec.width:(i+1)*sec.width]
+                cell_bytes = BitArray(cell)
+                if len(cell_bytes) < sec.width:  # we can assume content ends here
+                    if used == -1:
+                        used = len(binary)/sec.fill_bits
+                    f = '0' * (sec.bin_width - len(cell_bytes))
+                    cell_bytes.append(BitArray(bin=f))
+                cell_bytes = cell_bytes[len(cell_bytes)-sec.bin_width:]
+                binary.append(cell_bytes)
+                if parity > 0 and i % parity == 1:
+                    parity_bit = str(binary[-(i*parity*sec.bin_width):].bin.count('1') % 2)
+                    binary.append(BitArray(bin=parity_bit))
+
+            if used == -1:  # this is bad. Content is bigger than memory itself
+                used = len(binary)/sec.fill_bits
+            # if fill_bits > 0 and len(binary) < fill_bits:
+            #     used = len(binary) / fill_bits
+            #     fill = (fill_bits - len(binary)) * BitArray(bin='0')
+            #     binary.append(fill)
+
+            with open(output, 'wb') as f:
+                f.write(binary.bytes)
+            print(f'Saved {sec_name} to "{output}", used {used*100:.2f}% [{int(len(binary)/8 * used)}B of {len(binary)//8}B]')
     else:
-        datax = data
-
-    fa = f'0{math.ceil(math.ceil(math.log2(len(datax))) / 4)}x'
-    a = [format(d, f'0{width * 2}x') for d in datax]
-    for i in range(int(len(a) / 8) + 1):
-        y = a[i * 8:(i + 1) * 8]
-        if len(y) > 0:
-            x += (' '.join(y) + ' ' * ((8 - len(y)) * 3) + '  // ' + format((i * 8 - 1) + len(y), fa) + '\n').encode()
-    return x
-
-
-def convert_to_mif(data, depth=32, width=1):
-    x = f'''-- auto-generated memory initialisation file
-DEPTH = {math.ceil(depth)};
-WIDTH = {width * 8};
-ADDRESS_RADIX = HEX;
-DATA_RADIX = HEX;
-CONTENT
-BEGIN
-'''.encode()
-
-    addr_format = f'0{math.ceil(int(math.log2(len(data))) / 4)}x'
-    if width == 2:
-        datax = [(x << 8) | y for x, y in zip(data[0::2], data[1::2])]
-        if len(data) % 2 == 1:
-            datax.append(data[-1] << 8)
-    else:
-        datax = data
-    a = [format(i, f'0{width * 2}x') for i in datax]
-    for i in range(int(len(a * width) / 8) + 1):
-        y = a[i * 8:(i + 1) * 8]
-        if len(y) > 0:
-            x += (format(i * 8, addr_format) + ' : ' + ' '.join(y) + ';\n').encode()
-    x += b"END;"
-    return x
+        print(f'Failed to compile {args.file}!')
+        sys.exit(1)
+    sys.exit(0)
+

+ 161 - 0
tools/format_utils.py

@@ -0,0 +1,161 @@
+import math
+from bitstring import BitArray
+
+
+def chunks(lst, n):
+    """Yield successive n-sized chunks from lst."""
+    for i in range(0, len(lst), n):
+        yield lst[i:i + n]
+
+
+def convert_to_mem(data, width, binary=False, reverse=False, packed=False):
+    """
+    Converts to general memory format
+    :param data: array of BinArray
+    :param width: width in bits
+    :param binary: output in binary values
+    :param reverse: reverse data order
+    :param packed: do not print any commens or spaces between values
+    :return: mem formatted text file
+    """
+    x = b''
+    if width % 8 != 0 and not binary:
+        raise ValueError("Cannot convert non integer byte width to hex")
+
+    if reverse:
+        data = reversed(data)
+    if packed:
+        arr = [(c.bin if binary else c.hex).encode() for c in data]
+        return b''.join(arr)
+
+    line_items = (8 if binary else 32)//(width//8)
+    fa = f'0{math.ceil(math.ceil(math.log2(len(data))) / 4)}x'
+    for i, chunk in enumerate(chunks(data, line_items)):
+        arr = [(c.bin if binary else c.hex).encode() for c in chunk]
+        x += b' '.join(arr) + f'  // {format(line_items*i, fa)}\n'.encode()
+    return x
+
+
+def convert_to_mif(data, width):
+    """
+    :param data: array of BinArray
+    :param width: width in bits
+    :return: mif formatted text file
+    """
+    radix = 'HEX' if width % 8 == 0 else 'BIN'
+    x = f'''-- auto-generated memory initialisation file
+-- total size: {sizeof_fmt(len(data) * width, 'bits', addi=False)}
+DEPTH = {len(data)};
+WIDTH = {width};
+ADDRESS_RADIX = HEX;
+DATA_RADIX = {radix};
+CONTENT
+BEGIN
+'''.encode()
+    line_items = (8 if radix == 'BIN' else 32)//(width//8)
+    depth = math.ceil(math.log2(len(data)))
+    addr_format = f'0{math.ceil(depth / 4)}x'
+    for i, comp in enumerate(chunks(data, line_items)):
+        a = ' '.join([c.bin if radix == 'BIN' else c.hex for c in comp])
+        x += f'{format(i*line_items, addr_format)} : {a};\n'.encode()
+    x += b"END;"
+    return x
+
+
+def sizeof_fmt(num, suffix='B', addi=True):
+    l = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi'] if addi else ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
+    for unit in l:
+        if abs(num) < 1024.0:
+            return "%3.1f%s%s" % (num, unit, suffix)
+        num /= 1024.0
+    return "%.1f%s%s" % (num, 'Yi' if addi else 'Y', suffix)
+
+
+# format function map. Function input: [array of BinArray, width in bits]
+FORMAT_MAP = {
+    'mif':  ('mif',  convert_to_mif, {}),
+    'ubin': ('ubin', convert_to_mem, {'binary': True, 'reverse': True, 'packed': True}),
+    'uhex': ('uhex', convert_to_mem, {'binary': False, 'reverse': True, 'packed': True}),
+    'memh': ('mem',  convert_to_mem, {'binary': False}),
+    'memb': ('mem',  convert_to_mem, {'binary': True}),
+}
+
+if __name__ == '__main__':
+    import argparse
+    import sys
+    from os import path, mkdir
+
+    parser = argparse.ArgumentParser(description='Program formatter', add_help=True)
+    parser.add_argument('file', help='Files to compile')
+    parser.add_argument('-o', '--output', help='Output directory')
+    parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
+    parser.add_argument('-s', '--stdout', action='store_true', help='Print to stdout')
+    parser.add_argument('-S', '--slice', type=int, default=0, help='Slice output')
+    parser.add_argument('-n', '--slice_no', type=int, default=-1, help='Output only nth slice')
+    parser.add_argument('-w', '--width', type=int, default=8, help='Data width in bits')
+    parser.add_argument('-t', '--output_type', choices=list(FORMAT_MAP.keys()), default='mem', help='Output type')
+
+    args = parser.parse_args(sys.argv[1:])
+    bname = path.basename(args.file).rsplit('.', 1)[0]
+
+    if args.width < 1:
+        print(f'Width must be more than 0', file=sys.stderr)
+        sys.exit(1)
+
+    if args.slice < 1:
+        args.slice = 1
+
+    if not path.isfile(args.file):
+        print(f'No file {args.file}!', file=sys.stderr)
+        sys.exit(1)
+
+    output_dir = args.output or path.dirname(args.file)
+    if not path.exists(output_dir):
+        mkdir(output_dir)
+
+    ifile = open(args.file, 'rb')
+    binary = BitArray(ifile.read())
+    ifile.close()
+
+    if args.slice > 1 and len(binary) % (args.slice*args.width) != 0:
+        print(f'File {args.file} (size {len(binary)}B) cannot be sliced into {args.slice} equal parts', file=sys.stderr)
+        sys.exit(1)
+
+    slices_bin = [binary[i * args.width:(i + 1) * args.width] for i in range(len(binary) // args.width)]
+    ext, func, kwargs = FORMAT_MAP[args.output_type]
+
+    for sno in range(args.slice):
+        if args.slice_no >= 0 and args.slice_no != sno:
+            continue
+        if args.slice == 1:
+            output = path.join(output_dir, f'{bname}.{ext}')
+        else:
+            output = path.join(output_dir, f'{bname}.{sno}.{ext}')
+
+        if not args.force and not args.stdout:
+            if path.isfile(output):
+                print(f'Output file already exists {output}!', file=sys.stderr)
+                inval = ''
+                while True:
+                    inval = input('Override? [y/n]: ')
+                    inval = inval.lower().strip()
+                    if inval != 'y' and inval != 'n':
+                        print('Please type y or n')
+                        continue
+                    break
+                if inval == 'n':
+                    continue
+
+        slice_bin = slices_bin[sno::args.slice]
+        try:
+            data = func(slice_bin, args.width, **kwargs)
+        except ValueError as e:
+            print(e.args[0])
+            continue
+
+        if args.stdout:
+            sys.stdout.write(data.decode())
+        else:
+            with open(output, 'wb') as f:
+                f.write(data)
+            print(f'Written {sizeof_fmt(len(slice_bin)*args.width, "bits", addi=False)} to {output}')

+ 131 - 124
tools/oisc8asm.py

@@ -1,11 +1,10 @@
 import argparse
 import sys
-import math
 from os import path, mkdir
 
 import asm_compiler as compiler
 from asm_compiler import InstructionError, CompilingError
-
+import format_utils
 
 class Compiler(compiler.Compiler):
     def compile(self, file, code):
@@ -145,126 +144,134 @@ asmc.add_instr(InstructionDest("COMD", 12))
 asmc.add_instr(InstructionDest("REG0", 13))
 asmc.add_instr(InstructionDest("REG1", 14))
 
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description='Assembly compiler', add_help=True)
-    parser.add_argument('file', help='Files to compile')
-    parser.add_argument('-t', '--output_type', choices=['bin', 'mem', 'binary', 'mif', 'uhex'], default='mem',
-                        help='Output type')
-    parser.add_argument('-S', '--slice', default=0, type=int, help='Slice output for section')
-    parser.add_argument('-o', '--output', help='Output directory')
-    parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
-    parser.add_argument('-s', '--stdout', action='store_true', help='Print to stdout')
-    parser.add_argument('-D', '--decompile', action='store_true', help='Print decompiled')
-    parser.add_argument('section', help='Section')
-    args = parser.parse_args(sys.argv[1:])
-    if not path.isfile(args.file):
-        print(f'No file {args.file}!')
-        sys.exit(1)
-
-    output_dir = args.output or path.dirname(args.file)
-    if not path.exists(output_dir):
-        mkdir(output_dir)
-
-    if args.output_type == 'mem' or args.output_type == 'binary':
-        ext = '.mem'
-    elif args.output_type == 'bin':
-        ext = '.bin'
-    elif args.output_type == 'mif':
-        ext = '.mif'
-    elif args.output_type == 'uhex':
-        ext = '.uhex'
-    else:
-        ext = '.out'
-    bname = path.basename(args.file).rsplit('.', 1)[0]
-
-    outputs = []
-    if args.slice > 0:
-        for i in range(0, args.slice):
-            outputs.append(path.join(output_dir, f'{bname}{args.section}_{i}{ext}'))
-    else:
-        outputs = [path.join(output_dir, bname + args.section + ext)]
-    if not args.stdout and not args.force:
-        for output in outputs:
-            if path.isfile(output):
-                print(f'Output file already exists {output}!')
-                sys.exit(1)
-
-    data = asmc.compile_file(args.file)
-    if data is not None:
-        section = args.section
-        if section in data:
-            width, length, size, bdata = data[section]
-            asize = len(bdata)
 
-            if size > 0:
-                bdataf = bdata + (size - len(bdata)) * bytearray(b'\x00')
-            else:
-                bdataf = bdata
-
-            if section == '.text':
-                if len(bdata) % 4 != 0:
-                    bdata += b'\x00\x00'
-                bitblocks = [
-                    format(int.from_bytes(bdataf[i * 4:i * 4 + 2], 'big'), '013b') +
-                    format(int.from_bytes(bdataf[i * 4 + 2:i * 4 + 4], 'big'), '013b')
-                    for i in range(0, len(bdataf) // 4)
-                ]
-                bitblocks += ['0'*26] * (size//3 - len(bitblocks))
-                # calculate parity bit
-                bitblocks = [b + str(b.count('1') % 2) for b in bitblocks]
-                # divide into 3 for each memory chip
-            else:
-                bits = [
-                    format(int.form_bytes(bdataf[i * width:i * width + width], 'big'), f'0{width*2}b')
-                    for i in range(0, len(bdataf) // width)
-                ]
-                bitblocks = [bdataf]
-                # bitstring = ''.join(bits)
-                # bitstring += len(bitstring) % 8 * 27 * '0'  # fill to the integer byte
-                # x = int(bitstring, 2).to_bytes(len(bitstring) // 8, 'big')
-
-            for i, output in enumerate(outputs):
-                block_chunks = []
-                chunk_width = 9 if section == '.text' else 16
-                for block in bitblocks:
-                    for j in range(len(outputs)):
-                        block_chunks.append(block[chunk_width * j:chunk_width * (j+1)])
-
-                y = block_chunks[i::len(outputs)]
-                if args.output_type == 'binary':
-                    x = '\n'.join(y).encode()
-                else:
-                    # merge bits chunks info bytes
-                    y = int(''.join(y), 2).to_bytes(len(y)//8, 'big')
-                    if args.output_type == 'mem':
-                        x = compiler.convert_to_mem(y, width=1, uhex=True, reverse=True)
-                    elif args.output_type == 'mif':
-                        x = compiler.convert_to_mif(y, width=width, depth=len(y)/width)
-                    elif args.output_type == 'uhex':
-                        x = compiler.convert_to_mem(y, width=width, uhex=True)
-                    else:
-                        x = bytes(y)
-
-                op = 'Printing' if args.stdout else 'Saving'
-                print(f"{op} {args.output_type} {section} data '{output}' [Size: {len(y)}B Slice: {i+1}/{len(outputs)}]")
-                if args.stdout:
-                    if args.decompile:
-                        print(asmc.decompile(bdata))
-                    else:
-                        print(x.decode())
-                else:
-                    with open(output, 'wb') as of:
-                        of.write(x)
-            if section == '.text':
-                insUsed = int(asize)//2
-                insTotal = size//2
-                print(f"Total {section} size: {insUsed / insTotal * 100:.1f}% "
-                      f"[ {insUsed} instr / {insTotal} instr ]")
-            else:
-                print(f"Total {section} size: {asize / len(bdataf) * 100:.1f}% [{int(asize)}B/{len(bdataf)}B]")
-        else:
-            print(f'No such section {section}!')
-    else:
-        print(f'Failed to compile {args.file}!')
-        sys.exit(1)
-    sys.exit(0)
+if __name__ == '__main__':
+    compiler.main(asmc)
+    # parser = argparse.ArgumentParser(description='Assembly compiler', add_help=True)
+    # parser.add_argument('file', help='Files to compile')
+    # parser.add_argument('-t', '--output_type', choices=['bin', 'mem', 'binary', 'mif', 'uhex', 'ubin'], default='mem',
+    #                     help='Output type')
+    # parser.add_argument('-S', '--slice', default=0, type=int, help='Slice output for section')
+    # parser.add_argument('-o', '--output', help='Output directory')
+    # parser.add_argument('-f', '--force', action='store_true', help='Force override output file')
+    # parser.add_argument('-s', '--stdout', action='store_true', help='Print to stdout')
+    # parser.add_argument('-D', '--decompile', action='store_true', help='Print decompiled')
+    # parser.add_argument('section', help='Section')
+    # args = parser.parse_args(sys.argv[1:])
+    # if not path.isfile(args.file):
+    #     print(f'No file {args.file}!')
+    #     sys.exit(1)
+    #
+    # output_dir = args.output or path.dirname(args.file)
+    # if not path.exists(output_dir):
+    #     mkdir(output_dir)
+    #
+    # if args.output_type == 'mem' or args.output_type == 'binary':
+    #     ext = '.mem'
+    # elif args.output_type == 'bin':
+    #     ext = '.bin'
+    # elif args.output_type == 'mif':
+    #     ext = '.mif'
+    # elif args.output_type == 'uhex':
+    #     ext = '.uhex'
+    # elif args.output_type == 'ubin':
+    #     ext = '.ubin'
+    # else:
+    #     ext = '.out'
+    # bname = path.basename(args.file).rsplit('.', 1)[0]
+    #
+    # outputs = []
+    # if args.slice > 0:
+    #     for i in range(0, args.slice):
+    #         outputs.append(path.join(output_dir, f'{bname}{args.section}_{i}{ext}'))
+    # else:
+    #     outputs = [path.join(output_dir, bname + args.section + ext)]
+    # if not args.stdout and not args.force:
+    #     for output in outputs:
+    #         if path.isfile(output):
+    #             print(f'Output file already exists {output}!')
+    #             sys.exit(1)
+    #
+    # data = asmc.compile_file(args.file)
+    # if data is not None:
+    #     section = args.section
+    #     if section in data:
+    #         width, length, size, bdata = data[section]
+    #         asize = len(bdata)
+    #
+    #         if size > 0:
+    #             bdataf = bdata + (size - len(bdata)) * bytearray(b'\x00')
+    #         else:
+    #             bdataf = bdata
+    #
+    #         bitwidth = width * 8
+    #         if section == '.text':
+    #             if len(bdata) % 4 != 0:
+    #                 bdata += b'\x00\x00'
+    #             bitblocks = [
+    #                 format(int.from_bytes(bdataf[i * 4:i * 4 + 2], 'big'), f'013b') +
+    #                 format(int.from_bytes(bdataf[i * 4 + 2:i * 4 + 4], 'big'), f'013b')
+    #                 for i in range(0, len(bdataf) // 4)
+    #             ]
+    #             bitblocks += ['0'*26] * (size//3 - len(bitblocks))
+    #             # calculate parity bit
+    #             bitblocks = [b + str(b.count('1') % 2) for b in bitblocks]
+    #             # divide into 3 for each memory chip
+    #         else:
+    #             bitblocks = [
+    #                 format(int.from_bytes(bdataf[i * width:i * width + width], 'big'), f'0{bitwidth}b')
+    #                 for i in range(len(bdataf) // width)
+    #             ]
+    #             # bitblocks = [bdataf]
+    #             # bitstring = ''.join(bits)
+    #             # bitstring += len(bitstring) % 8 * 27 * '0'  # fill to the integer byte
+    #             # x = int(bitstring, 2).to_bytes(len(bitstring) // 8, 'big')
+    #
+    #         for i, output in enumerate(outputs):
+    #             block_chunks = []
+    #             chunk_width = 9 if section == '.text' else 16
+    #             for block in bitblocks:
+    #                 for j in range(len(outputs)):
+    #                     block_chunks.append(block[chunk_width * j:chunk_width * (j+1)])
+    #
+    #             y = block_chunks[i::len(outputs)]
+    #             if args.output_type == 'binary':
+    #                 x = '\n'.join(y).encode()
+    #             elif args.output_type == 'mif':
+    #                 x = format_utils.convert_to_mif(y, width=bitwidth)
+    #             else:
+    #                 from bitstring import BitArray
+    #                 # merge bits chunks info bytes
+    #                 y = BitArray(bin='0b'+''.join(y)).bytes
+    #                 if args.output_type == 'mem':
+    #                     x = format_utils.convert_to_mem(y, width=1, uhex=True, reverse=True)
+    #                 elif args.output_type == 'uhex':
+    #                     x = format_utils.convert_to_mem(y, width=width, uhex=True)
+    #                 elif args.output_type == 'ubin':
+    #                     x = format_utils.convert_to_binary(reversed(y), width=width)
+    #                 else:
+    #                     x = bytes(y)
+    #
+    #             op = 'Printing' if args.stdout else 'Saving'
+    #             print(f"{op} {args.output_type} {section} data '{output}' [Size: {len(y)}B Slice: {i+1}/{len(outputs)}]")
+    #             if args.stdout:
+    #                 if args.decompile:
+    #                     print(asmc.decompile(bdata))
+    #                 else:
+    #                     print(x.decode())
+    #             else:
+    #                 with open(output, 'wb') as of:
+    #                     of.write(x)
+    #         if section == '.text':
+    #             insUsed = int(asize)//2
+    #             insTotal = size//2
+    #             print(f"Total {section} size: {insUsed / insTotal * 100:.1f}% "
+    #                   f"[ {insUsed} instr / {insTotal} instr ]")
+    #         else:
+    #             print(f"Total {section} size: {asize / len(bdataf) * 100:.1f}% [{int(asize)}B/{len(bdataf)}B]")
+    #     else:
+    #         print(f'No such section {section}!')
+    # else:
+    #     print(f'Failed to compile {args.file}!')
+    #     sys.exit(1)
+    # sys.exit(0)