From 1966ab78b45aa0b43fad8bd2b7701de9ed320476 Mon Sep 17 00:00:00 2001 From: "(Tim) Efthimis Kritikos" Date: Fri, 9 Feb 2024 23:28:21 +0000 Subject: [PATCH] Peripherals/I2C: Added a CPU I2C driver and wrote a bootloader that uses that to boot from an I2C eeprom I'm happy to have reached 200 commits and with this, version v0.3.0 is functionally ready. I still need to do a fair bit of cleanup and bug fixing though before the actual release. With this commit I added a CPU I2C driver as well as a basic arbiter to have the hardware lcd controller and the software i2c communication pass through the same I2C driver and I2C bus. I also wrote a bootloader that reads code from an i2c eeprom to make sure the hardware works. --- Makefile | 13 +- boot_code/Makefile | 3 +- boot_code/brainfuck_mandelbrot.asm | 4 +- boot_code/i2c_bootloader.asm | 118 +++++++++++++++ common.mk | 1 + system/Makefile | 7 +- .../fpga_config/OrangeCrab_r0.2.1/config.mk | 8 +- .../fpga_config/OrangeCrab_r0.2.1/fpga_top.v | 135 +++++++++++++++--- system/memory.v | 2 +- system/peripherals/CPU_to_I2C_driver_bridge.v | 91 ++++++++++++ system/peripherals/I2C_driver.v | 99 +++++++++++-- system/peripherals/I2C_driver_multiplexer.v | 118 +++++++++++++++ system/peripherals/pcf8574_for_HD44780.v | 33 ++--- 13 files changed, 576 insertions(+), 56 deletions(-) create mode 100644 boot_code/i2c_bootloader.asm create mode 100644 system/peripherals/CPU_to_I2C_driver_bridge.v create mode 100644 system/peripherals/I2C_driver_multiplexer.v 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