diff --git a/Makefile b/Makefile
index b265d9e..2af9250 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ VERILATOR_BIN=system/obj_dir/Vsystem
BOOT_CODE=boot_code/brainfuck_mandelbrot.txt
GTKWSAVE=./gtkwave_savefile.gtkw
MICROCODE=system/ucode.txt
-BOOTABLES=boot_code/brainfuck_compiled.txt boot_code/brainfuck_interpreted.txt boot_code/pipeline_ideal.txt boot_code/fibonacci.txt boot_code/gnome_sort.txt boot_code/cache_fill_and_empty.txt ${BOOT_CODE} boot_code/colored_led.txt boot_code/bios.stxt
+BOOTABLES=boot_code/brainfuck_compiled.txt boot_code/brainfuck_interpreted.txt boot_code/pipeline_ideal.txt boot_code/fibonacci.txt boot_code/gnome_sort.txt boot_code/cache_fill_and_empty.txt ${BOOT_CODE} boot_code/colored_led.txt boot_code/bios.stxt boot_code/i2c_bootloader.stxt
PRINT_PATH_PREFIX=.
NO_ASM=1
@@ -55,11 +55,16 @@ mrproper: clean
${Q}make ${MAKEOPTS} PRINT_PATH_PREFIX=boot_code/ -C boot_code mrproper
.PHONY: upload_bitstream
-upload_bitstream: boot_code/bios.stxt
+upload_bitstream: boot_code/i2c_bootloader.stxt
${Q}make ${MAKEOPTS} PRINT_PATH_PREFIX=system/ -C system upload_bitstream
+.PHONY: upload_bootrom
+upload_bootrom: boot_code/brainfuck_mandelbrot.bin
+ ${Q}make ${MAKEOPTS} PRINT_PATH_PREFIX=system/ -C system upload_bootrom
+
+
.PHONY: fpga_sim
-fpga_sim: boot_code/bios.stxt
+fpga_sim: boot_code/i2c_bootloader.stxt
${Q}make ${MAKEOPTS} PRINT_PATH_PREFIX=system/ -C system fpga_sim
.PHONY: help
@@ -76,6 +81,8 @@ help:
@echo 'Synthesis targets:'
@echo ' upload_bitstream - Build a bitstream for the board selected in'
@echo ' common.mk and upload it if possible'
+ @echo ' upload_bootrom - Build the rom file and upload it to an I2C rom'
+ @echo ' Currently only the MiniPro programmer is supported'
@echo ' fpga_sim - Simulate the SoC that gets build for the configured'
@echo ' fpga board'
@echo ''
diff --git a/boot_code/Makefile b/boot_code/Makefile
index 40c56b9..44edbc3 100644
--- a/boot_code/Makefile
+++ b/boot_code/Makefile
@@ -18,7 +18,7 @@
PRINT_PATH_PREFIX=./
SOURCE_FULL=brainfuck_interpreted.asm brainfuck_compiled.asm brainfuck_mandelbrot.asm pipeline_ideal.asm fibonacci.asm gnome_sort.asm cache_fill_and_empty.asm colored_led.asm
-SOURCE_SHORT=bios.asm
+SOURCE_SHORT=bios.asm i2c_bootloader.asm
BINARIES=$(subst .asm,.txt,${SOURCE_FULL}) $(subst .asm,.stxt,${SOURCE_SHORT})
BUILD_FILES=${BINARIES}
BUILD_FILES+=$(subst .asm,.memdump,${SOURCE_FULL} ${SOURCE_SHORT})
@@ -33,6 +33,7 @@ brainfuck_compiled.bin: brainfuck_compiler_v1.asm hello_9086.bf.asm dos_layer.as
brainfuck_mandelbrot.bin: brainfuck_compiler_v1.asm mandelbrot.bf.asm dos_layer.asm
colored_led.bin: dos_layer.asm
bios.bin: LiteDram_init.asm brainfuck_compiler_v1.asm hello_9086.bf.asm dos_layer.asm
+i2c_bootloader.bin: LiteDram_init.asm
fibonacci.bin: helpers.asm
gnome_sort.bin: helpers.asm
diff --git a/boot_code/brainfuck_mandelbrot.asm b/boot_code/brainfuck_mandelbrot.asm
index ecce2d3..aada192 100644
--- a/boot_code/brainfuck_mandelbrot.asm
+++ b/boot_code/brainfuck_mandelbrot.asm
@@ -3,7 +3,7 @@
org 0x100
output_program:
-org 0xD000
+org 0xC000
mov sp,#SMALL_STACK
call INIT_INT_VECT_TABLE
INCLUDE brainfuck_compiler_v1.asm
@@ -15,7 +15,7 @@ INCLUDE mandelbrot.bf.asm
.ORG 0xFFF0
-MOV AX,#0xD000
+MOV AX,#0xC000
JMP AX
.ORG 0xFFFF
diff --git a/boot_code/i2c_bootloader.asm b/boot_code/i2c_bootloader.asm
new file mode 100644
index 0000000..87ce934
--- /dev/null
+++ b/boot_code/i2c_bootloader.asm
@@ -0,0 +1,118 @@
+.org 0xF800
+mov sp,#STACK
+call litedram_init
+
+
+mov bx,#load_i2c_txt
+call print
+
+;########### SET ROM ADDRESS ############
+mov al,#0x50
+outb #0x61
+
+mov ax,#0x0000 ; I2C ADDRESS
+outw #0x62
+
+mov al,#0x02 ; Write 16bit
+outb #0x63
+
+mov al,#0x00
+outb #0x60
+
+;wait_send: inw #0x62
+;test al,#0x01
+;jnz wait_send
+
+mov ax,#0x1FFE
+aa:
+dec ax
+jnz aa
+
+mov bx,#OK_loading_txt
+call print
+
+
+;############ READ 20% ##################
+
+mov bx,#0xD000
+mov di,#0x0000
+rom_read_loop:
+mov al,#0x05 ; Read, 8bit, ignore ack
+outb #0x63
+
+mov al,#0x00
+outb #0x60
+
+mov ax,#0x1FFE
+aa2:
+dec ax
+jnz aa2
+
+inw #0x60
+STOSB
+
+cmp di,#0x2999
+jz print_20
+cmp di,#0x5333
+jz print_40
+cmp di,#0x7CCC
+jz print_60
+cmp di,#0xA666
+jz print_80
+
+back:dec bx
+jnz rom_read_loop
+
+
+MOV AX,#0xC000
+JMP AX
+
+print_20:
+mov bx,#twenty_prc_txt
+call print
+jmp back
+
+print_40:
+mov bx,#forty_prc_txt
+call print
+jmp back
+
+print_60:
+mov bx,#sixty_prc_txt
+call print
+jmp back
+
+print_80:
+mov bx,#eighty_prc_txt
+call print
+jmp back
+
+
+include LiteDram_init.asm
+
+print:
+mov al,[bx]
+cmp al,#0
+je print_exit
+out byte #0xA5
+inc bx
+jmp print
+print_exit:
+ret
+
+ .BLKB 18 ; Using the text as stack space for the compiled program
+STACK: ; brainfuck_mandelbrot depends on stack being at the end
+
+load_i2c_txt: .ASCII 'Read I2C EEPROM:\0'
+OK_loading_txt: .ASCII 'OK\nLoading rom 0%\r\0'
+twenty_prc_txt: .ASCII 'Loading rom 20%\r\0'
+forty_prc_txt: .ASCII 'Loading rom 40%\r\0'
+sixty_prc_txt: .ASCII 'Loading rom 60%\r\0'
+eighty_prc_txt: .ASCII 'Loading rom 80%\r\0'
+
+.ORG 0xFFF0
+MOV AX,#0xF800
+JMP AX
+
+.ORG 0xFFFF
+DB 0x00 ;Make sure a full 64KiB image
diff --git a/common.mk b/common.mk
index 40c0197..1d23b7a 100644
--- a/common.mk
+++ b/common.mk
@@ -47,6 +47,7 @@ ifeq "${QUIET}" "1"
QUIET_ECPPACK = @echo ' ECPPACK '${PRINT_PATH_PREFIX}$@;
QUIET_DFU_SUFFIX = @echo ' DFU-SUFFIX '${PRINT_PATH_PREFIX}$@;
QUIET_DFU_UTIL = @echo ' DFU-UTIL '${PRINT_PATH_PREFIX}$<;
+ QUIET_MINIPRO = @echo ' MINIPRO '${PRINT_PATH_PREFIX}${ROM_FILE};
QUIET_DOWNLOAD = @echo ' DOWNLOAD '${PRINT_PATH_PREFIX}$@;
diff --git a/system/Makefile b/system/Makefile
index 3ad2979..28e3efa 100644
--- a/system/Makefile
+++ b/system/Makefile
@@ -100,9 +100,9 @@ ${BUILD_FILES_PREFIX}/sim_${FPGA_BOARD}/${VERILATOR_FPGA_BIN}.mk: fpga_config/${
${Q}verilator -DCALCULATE_IPC -DOUTPUT_JSON_STATISTICS --Mdir ${BUILD_FILES_PREFIX}/sim_${FPGA_BOARD}/ ${VERILATOR_OPTS} ../../fpga_config/${FPGA_BOARD}/testbench.cpp ${FPGA_SIM_SOURCES}
.PHONY: fpga_sim
-fpga_sim fpga_sim.fst: ../boot_code/bios.stxt ${BUILD_FILES_PREFIX}/sim_${FPGA_BOARD}/${VERILATOR_FPGA_BIN} ${MICROCODE} simplified_ucode.txt
+fpga_sim fpga_sim.fst: ../boot_code/i2c_bootloader.stxt ${BUILD_FILES_PREFIX}/sim_${FPGA_BOARD}/${VERILATOR_FPGA_BIN} ${MICROCODE} simplified_ucode.txt
$(call QUIET_VERILATOR_RUN,$(word 2,$^),$<)
- ${Q} ${NUMACTL} "${BUILD_FILES_PREFIX}/sim_${FPGA_BOARD}/${VERILATOR_FPGA_BIN}" +VERSION=${VERSION} +WAVEFORM="fpga_sim.fst" +COMMIT=${COMMIT} +BOOT_CODE="../boot_code/bios.stxt" +MICROCODE="simplified_ucode.txt"
+ ${Q} ${NUMACTL} "${BUILD_FILES_PREFIX}/sim_${FPGA_BOARD}/${VERILATOR_FPGA_BIN}" +VERSION=${VERSION} +WAVEFORM="fpga_sim.fst" +COMMIT=${COMMIT} +BOOT_CODE="../boot_code/i2c_bootloader.stxt" +MICROCODE="simplified_ucode.txt"
################################################################################
#### FPGA/ASIC RECIPES ####
@@ -157,6 +157,9 @@ dfu_upload:${BUILD_FILES_PREFIX}bitstream_${BUILD_NAME}.dfu
${QUIET_DFU_UTIL}
${Q}stdbuf -o0 dfu-util --download "$<" |stdbuf -o0 tr '\n' '\a' | stdbuf -o0 tr '\r' '\n' | grep Download --line-buffered | stdbuf -o0 tr '\n' '\r' |stdbuf -o0 tr '\a' '\n'
+minipro_upload:
+ ${QUIET_MINIPRO}
+ ${Q}minipro -p ${ROM_PART_ID} -w ${ROM_FILE}
################################################################################
#### CLEAN-UP ####
diff --git a/system/fpga_config/OrangeCrab_r0.2.1/config.mk b/system/fpga_config/OrangeCrab_r0.2.1/config.mk
index 8906b13..91799e8 100644
--- a/system/fpga_config/OrangeCrab_r0.2.1/config.mk
+++ b/system/fpga_config/OrangeCrab_r0.2.1/config.mk
@@ -15,18 +15,22 @@ FPGA_FILE_EXT=dfu
# 6 is the slowest and 8 is the fastest
ECP5_SPEED_GRADE=8
+ROM_PART_ID=24lc512
######## End of user configuration ########
#NOT USED OUTSIDE OF HERE
-FPGA_SOC_COMMON_SOURCES=peripherals/I2C_driver.v peripherals/ascii_to_HD44780_driver.v peripherals/pcf8574_for_HD44780.v peripherals/Wishbone_IO_driver.v peripherals/Wishbone_memory_driver.v
+FPGA_SOC_COMMON_SOURCES=peripherals/I2C_driver.v peripherals/ascii_to_HD44780_driver.v peripherals/pcf8574_for_HD44780.v peripherals/Wishbone_IO_driver.v peripherals/Wishbone_memory_driver.v peripherals/I2C_driver_multiplexer.v peripherals/CPU_to_I2C_driver_bridge.v
FPGA_SOC_SOURCES=${FPGA_SOC_COMMON_SOURCES} external_ip/litedram_core_ecp5_phy.v
FPGA_SOC_SIM_SOURCES=${FPGA_SOC_COMMON_SOURCES} fpga_config/OrangeCrab_r0.2.1/verilator_config.vlt external_ip/litedram_core_ecp5_phy_sim.v
-FPGA_BOOTCODE=../boot_code/bios.stxt
+FPGA_BOOTCODE=../boot_code/i2c_bootloader.stxt
+
+ROM_FILE=../boot_code/brainfuck_mandelbrot.bin
upload_bitstream: dfu_upload
+upload_bootrom: minipro_upload
${BUILD_FILES_PREFIX}bitstream_${BUILD_NAME}.bit:${BUILD_FILES_PREFIX}nextpnr-ecp5_${BUILD_NAME}.bit
${Q}cp "$^" "$@"
diff --git a/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v b/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v
index 24e959e..2f5acde 100644
--- a/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v
+++ b/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v
@@ -193,16 +193,16 @@ end
// Cache to allow the slow display to have a
// chance to keep up with the relentless CPU
-reg [6:0] disp_cache_start=0;
-reg [6:0] disp_cache_end=0;
-reg [7:0] disp_write_cache [127:0];
+reg [7:0] disp_cache_start=0;
+reg [7:0] disp_cache_end=0;
+reg [7:0] disp_write_cache [255:0];
reg ascii_state=0;
always @(posedge CPU_SPEED)begin
if(wr==0)begin
if(IOMEM==1'b1 && address_bus[7:0]==8'hA5 )begin
disp_write_cache[disp_cache_end]<=data_bus_write[15:8];
- disp_cache_end<=disp_cache_end+7'd1;
+ disp_cache_end<=disp_cache_end+8'd1;
end else if(IOMEM==1'b1 && address_bus[7:0]==8'hB0 )begin
if(data_bus_write[0:0]==1)
rgb_led_color<=3'b000;
@@ -215,7 +215,7 @@ always @(posedge CPU_SPEED)begin
$write("%s" ,disp_write_cache[disp_cache_start]); // TODO: maybe simulate the i2c lcd
`endif
ascii_data<=disp_write_cache[disp_cache_start];
- disp_cache_start<=disp_cache_start+7'd1;
+ disp_cache_start<=disp_cache_start+8'd1;
ascii_data_write_req<=1;
ascii_state<=1'b1;
end
@@ -245,12 +245,12 @@ doublemem #( .RAM_SIZE_IN_BYTES(2048) ) boot_rom
/// Memory Map
assign data_bus_read = ( IOMEM == 1 ) ? data_bus_read_IO : data_bus_read_MEM ;
-wire [15:0] data_bus_read_IO = ( address_bus[7:1] == 7'h10 ) ? LITEDRAM_STATUS_REGISTER : Wishbone_driver_data_bus_read;
+wire [15:0] data_bus_read_IO = ( address_bus[7:1] == 7'h10 ) ? LITEDRAM_STATUS_REGISTER : ( (address_bus[7:4] == 4'h4) ? Wishbone_driver_data_bus_read:CPU_I2C_data_bus_read);
assign boot_rom_cs_n = !((IOMEM==0)&&(address_bus[15:12]==4'hF));
-wire [15:0] data_bus_read_MEM = (address_bus[15:12]==4'hF)?boot_rom_data_bus_read:data_bus_read_DDR3;
+wire [15:0] data_bus_read_MEM = (address_bus[15:12]==4'hF)? boot_rom_data_bus_read:data_bus_read_DDR3;
wire DDR3_ram_cs = (IOMEM==0)&&(address_bus[15:12]!=4'hF);
//
@@ -264,7 +264,17 @@ wire DDR3_ram_cs = (IOMEM==0)&&(address_bus[15:12]!=4'hF);
// 0x3F | WISHBONE |
//
assign wishbone_cs=!((IOMEM==1)&&(address_bus[7:4] == 4'h4));
+assign i2c_cs= !((IOMEM==1)&&(address_bus[7:4] == 4'h6));
+assign wait_state_I2C=(i2c_cs==1'b0&&rd==1'b0)^i2c_cs_1d;
+
+reg i2c_cs_1d;
+
+always @(posedge CPU_SPEED)begin
+ i2c_cs_1d<=(i2c_cs==1'b0&&rd==1'b0);
+end // TODO: Probably would need to so the same for the wishbone IO
+
+wire wait_state_I2C;
/// DDR3 Controller
wire ddr3_init_done,ddr3_init_error,ddr3_pll_locked;
@@ -323,7 +333,7 @@ Wishbone_memory_driver Wishbone_memory_driver(
.BHE(BHE),
.data_bus_in(data_bus_write),
.data_bus_out(data_bus_read_DDR3),
- .wait_state(wait_state),
+ .wait_state(wait_state_WBIO),
.read_n(rd),
.write_n(wr),
.chip_select_n(!(DDR3_ram_cs&&ddr3_init_done)),
@@ -341,6 +351,8 @@ Wishbone_memory_driver Wishbone_memory_driver(
);
wire wait_state;
+assign wait_state=wait_state_WBIO||wait_state_I2C;
+wire wait_state_WBIO;
wire wb_ctrl_ack;
wire [29:0] wb_ctrl_adr;
@@ -422,6 +434,38 @@ litedram_core DDR3_RAM_DRIVER(
.wb_ctrl_dat_w(wb_ctrl_dat_w)
);
+wire [15:0]CPU_I2C_data_bus_read;
+wire i2c_cs;
+
+wire [6:0] CPU_I2C_OUT_ADDRESS;
+wire CPU_I2C_OUT_BUSY,CPU_I2C_OUT_TRANSACT,CPU_I2C_DIR;
+wire [15:0]CPU_I2C_DATA_READ,CPU_I2C_DATA_WRITE;
+wire CPU_I2C_IGN_ACK;
+
+CPU_to_I2C_driver_bridge CPU_to_I2C_driver_bridge (
+ .clock(CPU_SPEED),
+ .reset_n(reset),
+
+ // CPU INTERFACE
+ .address(address_bus[2:0]),
+ .data_bus_in(data_bus_write),
+ .data_bus_out(CPU_I2C_data_bus_read),
+ .read_n(rd),
+ .write_n(wr),
+ .chip_select_n(i2c_cs),
+
+ // I2C DRIVER INTERFACE
+ .OUT_ADDRESS(CPU_I2C_OUT_ADDRESS),
+ .OUT_BUSY(CPU_I2C_OUT_BUSY),
+ .OUT_TRANSACT(CPU_I2C_OUT_TRANSACT),
+ .DIR(CPU_I2C_DIR),
+ .OUT_I2C_DATA_READ(CPU_I2C_DATA_READ),
+ .OUT_I2C_DATA_WRITE(CPU_I2C_DATA_WRITE),
+
+ .TRANS_WIDTH(CPU_I2C_TRANS_WIDTH),
+ .OUT_IGN_ACK(CPU_I2C_IGN_ACK)
+);
+
// Display driver
wire ascii_data_ready;
@@ -448,7 +492,14 @@ ascii_to_HD44780_driver LCD_DRIVER(
wire pcf_write_req,pcf_command_data,pcf_busy;
wire [3:0]pcf_data;
-wire [7:0]i2c_data;
+/* verilator lint_off UNUSEDSIGNAL */
+wire [7:0]DISP_I2C_DATA_WRITE;
+wire [15:0]DISP_I2C_DATA_READ;
+/* verilator lint_on UNUSEDSIGNAL */
+wire DISP_I2C_BUSY,DISP_I2C_TRANSACT;
+wire [6:0] DISP_I2C_ADDRESS;
+assign DISP_I2C_ADDRESS=7'h27;
+wire DISP_DIR;
pcf8574_for_HD44780 PCF8574_driver(
.clock(I2C_SPEED),
@@ -461,16 +512,61 @@ pcf8574_for_HD44780 PCF8574_driver(
.new_backlight(1'b0),
.backlight_update(1'b0),
- .I2C_BUSY(I2C_BUSY),
- .I2C_SEND(I2C_SEND),
+ .DIR(DISP_DIR),
+ .I2C_BUSY(DISP_I2C_BUSY),
+ .I2C_TRANSACT(DISP_I2C_TRANSACT),
- .i2c_data(i2c_data)
+ .i2c_data_write(DISP_I2C_DATA_WRITE)
+);
+
+wire [6:0]MULT_TO_DRIV_I2C_ADDRESS;
+wire MULT_TO_DRIV_I2C_BUSY;
+wire MULT_TO_DRIV_I2C_TRANSACT;
+wire [15:0]MULT_TO_DRIV_DATA_WRITE, MULT_TO_DRIV_DATA_READ;
+wire MULT_TO_DRIV_DIR;
+wire CPU_I2C_TRANS_WIDTH;
+
+I2C_driver_multiplexer I2C_driver_multiplexer(
+ .clock(I2C_SPEED),
+ .reset_n(reset),
+
+ ////// INPUT 1 ///////
+ .IN1_ADDRESS(DISP_I2C_ADDRESS),
+ .IN1_BUSY(DISP_I2C_BUSY),
+ .IN1_DIR(DISP_DIR),
+ .IN1_TRANSACT(DISP_I2C_TRANSACT),
+ .IN1_I2C_DATA_READ(DISP_I2C_DATA_READ),
+ .IN1_I2C_DATA_WRITE({8'h0,DISP_I2C_DATA_WRITE}),
+ .IN1_TRANS_WIDTH(1'b0),
+ .IN1_IGN_ACK(1'b0),
+
+ ////// INPUT 2 ///////
+ .IN2_ADDRESS(CPU_I2C_OUT_ADDRESS),
+ .IN2_BUSY(CPU_I2C_OUT_BUSY),
+ .IN2_TRANSACT(CPU_I2C_OUT_TRANSACT),
+ .IN2_DIR(CPU_I2C_DIR),
+ .IN2_I2C_DATA_READ(CPU_I2C_DATA_READ),
+ .IN2_I2C_DATA_WRITE(CPU_I2C_DATA_WRITE),
+ .IN2_TRANS_WIDTH(CPU_I2C_TRANS_WIDTH),
+ .IN2_IGN_ACK(CPU_I2C_IGN_ACK),
+
+ ////// OUTPUT ///////
+ .OUT_ADDRESS(MULT_TO_DRIV_I2C_ADDRESS),
+ .OUT_BUSY(MULT_TO_DRIV_I2C_BUSY),
+ .OUT_TRANSACT(MULT_TO_DRIV_I2C_TRANSACT),
+ .OUT_DIR(MULT_TO_DRIV_DIR),
+ .OUT_I2C_DATA_WRITE(MULT_TO_DRIV_DATA_WRITE),
+ .OUT_I2C_DATA_READ(MULT_TO_DRIV_DATA_READ),
+ .OUT_TRANS_WIDTH(MULT_TO_DRIV_TRANS_WIDTH),
+ .OUT_IGN_ACK(MULT_TO_DRIV_IGN_ACK)
);
// I2C driver
wire SDA_direction;
-wire SCL,SDA_input,SDA_output,I2C_BUSY,I2C_SEND;
+wire SCL,SDA_input,SDA_output;
+wire MULT_TO_DRIV_TRANS_WIDTH;
+wire MULT_TO_DRIV_IGN_ACK;
I2C_driver i2c_driver(
.clock(I2C_SPEED),
@@ -480,10 +576,15 @@ I2C_driver i2c_driver(
.SDA_direction(SDA_direction),
.SCL(SCL),
- .address(7'h27),
- .I2C_BUSY(I2C_BUSY),
- .I2C_SEND(I2C_SEND),
- .i2c_data(i2c_data)
+ .address(MULT_TO_DRIV_I2C_ADDRESS),
+ .I2C_BUSY(MULT_TO_DRIV_I2C_BUSY),
+ .I2C_TRANSACT(MULT_TO_DRIV_I2C_TRANSACT),
+ .DIR(MULT_TO_DRIV_DIR),
+ .i2c_data_write(MULT_TO_DRIV_DATA_WRITE),
+ .i2c_data_read(MULT_TO_DRIV_DATA_READ),
+
+ .transact_width(MULT_TO_DRIV_TRANS_WIDTH),
+ .ignore_ack(MULT_TO_DRIV_IGN_ACK)
);
`ifdef SYNTHESIS
diff --git a/system/memory.v b/system/memory.v
index d233c75..a062665 100644
--- a/system/memory.v
+++ b/system/memory.v
@@ -48,7 +48,7 @@ initial begin
$readmemh(boot_code, memory,0,(RAM_SIZE_IN_BYTES/2)-1);
`else
//TODO: don't have it hard coded
- $readmemh("../boot_code/bios.stxt", memory,0,(RAM_SIZE_IN_BYTES/2)-1); // 2KiB
+ $readmemh("../boot_code/i2c_bootloader.stxt", memory,0,(RAM_SIZE_IN_BYTES/2)-1); // 2KiB
`endif
end
diff --git a/system/peripherals/CPU_to_I2C_driver_bridge.v b/system/peripherals/CPU_to_I2C_driver_bridge.v
new file mode 100644
index 0000000..8912266
--- /dev/null
+++ b/system/peripherals/CPU_to_I2C_driver_bridge.v
@@ -0,0 +1,91 @@
+/* CPU_to_I2C_driver_bridge - Implements CPU interface for the I2C_driver
+
+ This file is part of the 9086 project.
+
+ Copyright (c) 2024 Efthymios Kritikos
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+module CPU_to_I2C_driver_bridge (
+ input wire clock,
+ input wire reset_n,
+
+ // CPU INTERFACE
+ input wire [2:0] address,
+ input wire [15:0] data_bus_in,
+ output reg [15:0] data_bus_out,
+ input read_n,
+ input write_n,
+ input chip_select_n,
+
+ // I2C DRIVER INTERFACE
+ output wire [6:0] OUT_ADDRESS,
+ input wire OUT_BUSY,
+ output reg OUT_TRANSACT,
+ output reg DIR,
+ input wire [15:0] OUT_I2C_DATA_READ,
+ output reg [15:0] OUT_I2C_DATA_WRITE,
+
+ output reg TRANS_WIDTH,
+ output reg OUT_IGN_ACK
+);
+
+//assign data_bus_out=((address==3'h0)&&chip_select_n==1'b0&&read_n==1'b0)? 16'h0043 : 16'h0000;
+//assign data_bus_out= (chip_select_n==1'b0&&read_n==1'b0) ? 16'h0043 : 16'h0000;
+//assign data_bus_out= (1) ? 16'h0043 : 16'h0000;
+
+reg [6:0] ADDRESS_REG=7'b1111111;
+
+assign OUT_ADDRESS=ADDRESS_REG;
+
+/* verilator lint_off UNUSEDSIGNAL */
+reg [15:0]READ_DATA=16'h0;
+/* verilator lint_on UNUSEDSIGNAL */
+
+reg WAIT;
+always @( posedge clock )begin
+ if ( reset_n==0 ) begin
+ WAIT<=0;
+ OUT_TRANSACT<=0;
+ end
+ if ( WAIT==1'b1 && OUT_BUSY==1'b1 )begin
+ WAIT<=1'b0;
+ OUT_TRANSACT<=0;
+ READ_DATA<=OUT_I2C_DATA_READ;
+ end
+ if ( chip_select_n==0 && read_n==0 )begin
+ case (address)
+ 3'h0: data_bus_out <= {OUT_I2C_DATA_READ};
+ 3'h2: data_bus_out <= {15'd0,OUT_BUSY|WAIT};
+ default: data_bus_out <= 16'h0;
+ endcase
+ end
+ if ( chip_select_n==0 && write_n==0 && address==3'd3 ) begin
+ OUT_TRANSACT<=1;
+ WAIT<=1;
+ end
+ if ( chip_select_n==0 && write_n==0 && address==3'd3 ) begin
+ DIR<=data_bus_in[8:8];
+ TRANS_WIDTH<=data_bus_in[9:9];
+ OUT_IGN_ACK<=data_bus_in[10:10];
+ end
+ if ( chip_select_n==0 && write_n==0 && address==3'd1 ) begin
+ ADDRESS_REG <= data_bus_in[14:8];
+ end
+ if ( chip_select_n==0 && write_n==0 && address==3'd2 ) begin
+ OUT_I2C_DATA_WRITE <= data_bus_in[15:0];
+ end
+end
+
+endmodule
diff --git a/system/peripherals/I2C_driver.v b/system/peripherals/I2C_driver.v
index 2c674e8..bf61a2a 100644
--- a/system/peripherals/I2C_driver.v
+++ b/system/peripherals/I2C_driver.v
@@ -26,10 +26,18 @@ module I2C_driver (
input wire [6:0] address,
output reg I2C_BUSY=0,
- input wire I2C_SEND,
- input wire [7:0] i2c_data
+ input wire I2C_TRANSACT,
+ input wire DIR,
+ input wire [15:0] i2c_data_write,
+ input wire transact_width, /* 0=byte 1=word */
+ input wire ignore_ack,
+ output reg [15:0] i2c_data_read=16'h4141
);
+//assign i2c_data_read=16'h0042;
+
+reg DIR_latched;
+
reg SDA;
assign SDA_output=SDA;
@@ -37,9 +45,11 @@ assign SDA_output=SDA;
reg [5:0] i2c_state = 6'b100100;
reg [3:0] data_bit_counter;
-reg [7:0] data_internal;
+reg [15:0] data_internal;
reg [6:0]address_internal;
+reg trans_width_latch;
+
always @(posedge clock) begin
case (i2c_state)
/***** start sequence ******/
@@ -90,7 +100,7 @@ always @(posedge clock) begin
end
6'b001000:begin
SCL<=0;
- SDA<=0;/*Write=0 Read=1*/
+ SDA<=DIR_latched;/*Write=0 Read=1*/
i2c_state<=6'b001001;
end
6'b001001:begin
@@ -151,27 +161,39 @@ always @(posedge clock) begin
end
/****** Send data ********/
6'b010100:begin
+ if(DIR_latched==1'b1)begin
+ SDA_direction<=0;
+ end else begin
+ SDA_direction<=1;
+ end
SCL<=0;
- SDA_direction<=1;
i2c_state<=6'b010101;
end
6'b010101:begin
SCL<=0;
- SDA<=data_internal[7:7];
- data_internal[7:0]<={data_internal[6:0],1'b0};
+ if(DIR_latched==1'b0)begin
+ SDA<=data_internal[7:7];
+ data_internal[7:0]<={data_internal[6:0],1'b0};
+ end
data_bit_counter<=data_bit_counter+1;
i2c_state<=6'b010110;
end
6'b010110:begin
SCL<=1;
+ if(DIR_latched==1'b1)begin
+ //i2c_data_read[15:0]<={8'h0,SDA_input,i2c_data_read[7:1]};
+
+ i2c_data_read[15:0]<={8'h0,i2c_data_read[6:0],SDA_input};
+ end
i2c_state<=6'b010111;
end
6'b010111:begin
SCL<=1;
if(data_bit_counter==4'd8)begin
i2c_state<=6'b011000;
- end else
+ end else begin
i2c_state<=6'b010100;
+ end
end
/****** Acknowledge ********/
6'b011000:begin
@@ -189,11 +211,62 @@ always @(posedge clock) begin
end
6'b011011:begin
SCL<=1;
- if (SDA_input==1)begin
- i2c_state<=6'b111111;
+ if (SDA_input==0||ignore_ack==1'b1)begin
+ if(trans_width_latch==1'b1)begin
+ i2c_state<=6'b100101;
+ data_bit_counter<=4'd0;
+ end else
+ i2c_state<=6'b011100;
+ SDA_direction<=1;
end else begin
+ i2c_state<=6'b111111;
+ end
+ end
+ /****** Send data (16bit) ********/
+ 6'b100101:begin
+ SCL<=0;
+ SDA_direction<=1;
+ i2c_state<=6'b100110;
+ end
+ 6'b100110:begin
+ SCL<=0;
+ SDA<=data_internal[15:15];
+ data_internal[15:8]<={data_internal[14:8],1'b0};
+ data_bit_counter<=data_bit_counter+1;
+ i2c_state<=6'b100111;
+ end
+ 6'b100111:begin
+ SCL<=1;
+ i2c_state<=6'b101000;
+ end
+ 6'b101000:begin
+ SCL<=1;
+ if(data_bit_counter==4'd8)begin
+ i2c_state<=6'b101001;
+ end else
+ i2c_state<=6'b100101;
+ end
+ /****** Acknowledge (16bit) ********/
+ 6'b101001:begin
+ SDA_direction<=0;
+ SCL<=0;
+ i2c_state<=6'b101010;
+ end
+ 6'b101010:begin
+ SCL<=0;
+ i2c_state<=6'b101011;
+ end
+ 6'b101011:begin
+ SCL<=1;
+ i2c_state<=6'b101100;
+ end
+ 6'b101100:begin
+ SCL<=1;
+ if (SDA_input==0||ignore_ack==1'b1)begin
i2c_state<=6'b011100;
SDA_direction<=1;
+ end else begin
+ i2c_state<=6'b111111;
end
end
/****** separator ********/
@@ -239,11 +312,13 @@ always @(posedge clock) begin
I2C_BUSY<=0;
end
6'b100100:begin
- if(I2C_SEND==1)begin
+ if(I2C_TRANSACT==1)begin
I2C_BUSY<=1;
i2c_state<=0;
- data_internal<=i2c_data;
+ data_internal<=i2c_data_write;
address_internal<=address;
+ trans_width_latch<=transact_width;
+ DIR_latched<=DIR;
end
end
default:begin
diff --git a/system/peripherals/I2C_driver_multiplexer.v b/system/peripherals/I2C_driver_multiplexer.v
new file mode 100644
index 0000000..8be64ef
--- /dev/null
+++ b/system/peripherals/I2C_driver_multiplexer.v
@@ -0,0 +1,118 @@
+/* I2C_driver_multiplexer.v - Implements a multiplexer for the SoC side of I2C_driver.v
+
+ This file is part of the 9086 project.
+
+ Copyright (c) 2024 Efthymios Kritikos
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see . */
+
+module I2C_driver_multiplexer (
+ input wire clock,
+ input wire reset_n,
+
+ ////// INPUT 1 ///////
+ input wire [6:0] IN1_ADDRESS,
+ output reg IN1_BUSY,
+ input wire IN1_TRANSACT,
+ input wire IN1_DIR,
+ output reg [15:0] IN1_I2C_DATA_READ,
+ input wire [15:0] IN1_I2C_DATA_WRITE,
+ input IN1_TRANS_WIDTH,
+ input IN1_IGN_ACK,
+
+ ////// INPUT 2 ///////
+ input wire [6:0] IN2_ADDRESS,
+ output reg IN2_BUSY,
+ input wire IN2_TRANSACT,
+ input wire IN2_DIR,
+ output reg [15:0] IN2_I2C_DATA_READ,
+ input wire [15:0] IN2_I2C_DATA_WRITE,
+ input IN2_TRANS_WIDTH,
+ input IN2_IGN_ACK,
+
+ ////// OUTPUT ///////
+ output wire [6:0] OUT_ADDRESS,
+ input wire OUT_BUSY,
+ output reg OUT_TRANSACT,
+ output wire OUT_DIR,
+ input wire [15:0] OUT_I2C_DATA_READ,
+ output wire [15:0] OUT_I2C_DATA_WRITE,
+ output OUT_TRANS_WIDTH,
+ output wire OUT_IGN_ACK
+);
+
+reg select;
+
+assign OUT_TRANS_WIDTH = select ? IN1_TRANS_WIDTH : IN2_TRANS_WIDTH;
+assign OUT_I2C_DATA_WRITE = select ? IN1_I2C_DATA_WRITE : IN2_I2C_DATA_WRITE;
+assign OUT_ADDRESS = select ? IN1_ADDRESS : IN2_ADDRESS;
+assign OUT_IGN_ACK = select ? IN1_IGN_ACK : IN2_IGN_ACK;
+assign OUT_DIR= select ? IN1_DIR : IN2_DIR;
+
+reg [1:0] STATE;
+reg SERVICED;
+
+always @(posedge clock)begin
+ if(reset_n==1'b0)begin
+ OUT_TRANSACT<=0;
+ IN1_BUSY<=0;
+ IN2_BUSY<=0;
+ STATE<=2'd0;
+ end else begin
+ case(STATE)
+ 2'd0:begin
+ if(IN1_TRANSACT&&OUT_BUSY==1'b0)begin
+ select<=1'b1;
+ OUT_TRANSACT<=1;
+ STATE<=2'd1;
+ SERVICED<=1'b0;
+ end else if(IN2_TRANSACT&&OUT_BUSY==1'b0)begin
+ select<=1'b0;
+ OUT_TRANSACT<=1;
+ IN1_BUSY<=1;
+ STATE<=2'd1;
+ SERVICED<=1'b1;
+ end
+ if(OUT_BUSY==1'b0)begin
+ IN1_BUSY<=0;
+ IN2_BUSY<=0;
+ end
+ end
+ 2'd1:begin
+ if(OUT_BUSY==1'b1)begin
+ STATE<=2'd0;
+ OUT_TRANSACT<=0;
+ end
+ if(SERVICED==1'b0)begin
+ IN1_BUSY<=1;
+ end else begin
+ IN2_BUSY<=1;
+ end
+ end
+ default:begin
+ STATE<=2'b0;
+ end
+ endcase
+ end
+end
+
+always @(negedge OUT_BUSY)begin
+ if(select)begin
+ IN1_I2C_DATA_READ<=OUT_I2C_DATA_READ;
+ end else begin
+ IN2_I2C_DATA_READ<=OUT_I2C_DATA_READ;
+ end
+end
+
+endmodule
diff --git a/system/peripherals/pcf8574_for_HD44780.v b/system/peripherals/pcf8574_for_HD44780.v
index 20913e5..eaa9fec 100644
--- a/system/peripherals/pcf8574_for_HD44780.v
+++ b/system/peripherals/pcf8574_for_HD44780.v
@@ -32,9 +32,10 @@ input new_backlight,
input backlight_update,
input I2C_BUSY,
-output reg I2C_SEND=0,
+output reg I2C_TRANSACT=0,
+output reg DIR=0,
-output reg [7:0]i2c_data
+output reg [7:0]i2c_data_write
);
reg backlight=1;
@@ -49,52 +50,52 @@ always @(posedge clock) begin
4'b0000: begin /*Idle*/
if(pcf_write_req&&I2C_BUSY==0)begin
pcf_state<=4'b0001;
- I2C_SEND<=0;
+ I2C_TRANSACT<=0;
pcf_busy<=1;
end else if(backlight_latch&&I2C_BUSY==0)begin
pcf_state<=4'b1111;
- I2C_SEND<=0;
+ I2C_TRANSACT<=0;
pcf_busy<=1;
end
end
4'b0001: begin
if(I2C_BUSY==0)begin
- i2c_data<={pcf_data[3:0],backlight,1'b0,1'b0,pcf_command_data};
- I2C_SEND<=1;
+ i2c_data_write<={pcf_data[3:0],backlight,1'b0,1'b0,pcf_command_data};
+ I2C_TRANSACT<=1;
backlight_latch<=0;
pcf_state<=4'b0010;
end
end
4'b0010:begin
if(I2C_BUSY==1)begin
- I2C_SEND<=0;
+ I2C_TRANSACT<=0;
pcf_state<=4'b0011;
end
end
4'b0011: begin
if(I2C_BUSY==0)begin
- i2c_data<={pcf_data[3:0],backlight,1'b1,1'b0,pcf_command_data};
- I2C_SEND<=1;
+ i2c_data_write<={pcf_data[3:0],backlight,1'b1,1'b0,pcf_command_data};
+ I2C_TRANSACT<=1;
backlight_latch<=0;
pcf_state<=4'b0100;
end
end
4'b0100:begin
if(I2C_BUSY==1)begin
- I2C_SEND<=0;
+ I2C_TRANSACT<=0;
pcf_state<=4'b0101;
end
end
4'b0101: begin
if(I2C_BUSY==0)begin
- i2c_data<={pcf_data[3:0],backlight,1'b0,1'b0,pcf_command_data};
- I2C_SEND<=1;
+ i2c_data_write<={pcf_data[3:0],backlight,1'b0,1'b0,pcf_command_data};
+ I2C_TRANSACT<=1;
pcf_state<=4'b0110;
end
end
4'b0110: begin
if(I2C_BUSY==1)begin
- I2C_SEND<=0;
+ I2C_TRANSACT<=0;
pcf_state<=4'b0111;
end
end
@@ -107,12 +108,12 @@ always @(posedge clock) begin
4'b1111:begin
if(I2C_BUSY==1)begin
pcf_state<=4'b0000;
- I2C_SEND<=1'b0;
+ I2C_TRANSACT<=1'b0;
pcf_busy<=1'b0;
end else begin
- i2c_data<={4'b0,backlight,1'b0,1'b0,1'b0};
+ i2c_data_write<={4'b0,backlight,1'b0,1'b0,1'b0};
backlight_latch<=0;
- I2C_SEND<=1'b1;
+ I2C_TRANSACT<=1'b1;
pcf_busy<=1'b1;
end
end