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.
This commit is contained in:
(Tim) Efthimis Kritikos 2024-02-09 23:28:21 +00:00
parent 1d9be44c5a
commit 1966ab78b4
13 changed files with 576 additions and 56 deletions

View File

@ -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 ''

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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}$@;

View File

@ -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 ####

View File

@ -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 "$^" "$@"

View File

@ -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

View File

@ -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

View File

@ -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 <http://www.gnu.org/licenses/>. */
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

View File

@ -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,28 +161,40 @@ always @(posedge clock) begin
end
/****** Send data ********/
6'b010100:begin
SCL<=0;
if(DIR_latched==1'b1)begin
SDA_direction<=0;
end else begin
SDA_direction<=1;
end
SCL<=0;
i2c_state<=6'b010101;
end
6'b010101:begin
SCL<=0;
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
SDA_direction<=0;
@ -189,11 +211,62 @@ always @(posedge clock) begin
end
6'b011011:begin
SCL<=1;
if (SDA_input==1)begin
i2c_state<=6'b111111;
end else begin
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

View File

@ -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 <http://www.gnu.org/licenses/>. */
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

View File

@ -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