소스 검색

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)