From a88c420ca571dfd933d802d0435b0ee978a8f702 Mon Sep 17 00:00:00 2001 From: "(Tim) Efthimis Kritikos" Date: Thu, 9 Nov 2023 22:10:34 +0000 Subject: [PATCH] Added an I2C driver, a PCF8574 driver and an HD44780 display driver. Unfortunately this shows that even fibonacci doesn't run correctly. Nonetheless, I made colored_led.asm output text to the display! --- system/Makefile | 6 +- .../fpga_config/OrangeCrab_r0.2.1/fpga_top.v | 131 ++++++++- system/peripherals/I2C_driver.v | 258 ++++++++++++++++++ system/peripherals/ascii_to_HD44780_driver.v | 114 ++++++++ system/peripherals/pcf8574_for_HD44780.v | 123 +++++++++ 5 files changed, 617 insertions(+), 15 deletions(-) create mode 100644 system/peripherals/I2C_driver.v create mode 100644 system/peripherals/ascii_to_HD44780_driver.v create mode 100644 system/peripherals/pcf8574_for_HD44780.v diff --git a/system/Makefile b/system/Makefile index 945ba92..b426cba 100644 --- a/system/Makefile +++ b/system/Makefile @@ -69,10 +69,12 @@ endif ECP5_TARGETS=synth_ecp5.json synth_ecp5_out.config synth_ecp5.bit synth_ecp5.dfu ECP5_TARGETS+=abc.history # created from yosys +EXTRA_SYNTHESIS_SOURCES=peripherals/I2C_driver.v peripherals/ascii_to_HD44780_driver.v peripherals/pcf8574_for_HD44780.v + #TODO: we are relying on yosys to trim the input program txt file and hope its enough for the whole program... -synth_ecp5.json: ${SOURCES} ${TOP_LEVEL_SOURCE} fpga_config/${FPGA_BOARD}/fpga_top.v ${INCLUDES} ../boot_code/colored_led.txt +synth_ecp5.json: ${SOURCES} ${TOP_LEVEL_SOURCE} fpga_config/${FPGA_BOARD}/fpga_top.v ${EXTRA_SYNTHESIS_SOURCES} ${INCLUDES} ../boot_code/colored_led.txt ${QUIET_YOSYS} - ${Q} yosys -q -D BUILTIN_RAM=512 -D NOT_FULL -p 'read -sv '"${SOURCES} ${TOP_LEVEL_SOURCE} fpga_config/${FPGA_BOARD}/fpga_top.v ; synth_ecp5 -json $@" + ${Q} yosys -q -D BUILTIN_RAM=512 -D NOT_FULL -p 'read -sv '"${SOURCES} ${TOP_LEVEL_SOURCE} fpga_config/${FPGA_BOARD}/fpga_top.v ${EXTRA_SYNTHESIS_SOURCES} ; synth_ecp5 -json $@ -top fpga_top" synth_ecp5_out.config:synth_ecp5.json ${QUIET_NEXTPNR} 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 11b34a4..cbeaea1 100644 --- a/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v +++ b/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v @@ -28,6 +28,9 @@ module fpga_top( output rgb_led0_r, output rgb_led0_g, output rgb_led0_b, + + inout gpio_0,/*sda*/ + output gpio_1 /*scl*/ ); wire HALT; @@ -35,6 +38,7 @@ wire [`ERROR_BITS-1:0]ERROR; wire [19:0] address_bus; wire [15:0] data_bus_read,data_bus_write; wire rd,wr,BHE,IOMEM; + system system( /* MISC */ clk48,reset /* MEMORY / IO */ ,address_bus,data_bus_read,data_bus_write,BHE,rd,wr,IOMEM,HALT,ERROR @@ -45,15 +49,6 @@ assign rgb_led0_r=rgb_led_color[0]; assign rgb_led0_g=rgb_led_color[1]; assign rgb_led0_b=rgb_led_color[2]; -always @(negedge wr) begin - if(IOMEM==1'b1 && address_bus[7:0]==8'hB0 )begin - if(data_bus_write[0:0]==1) - rgb_led_color=3'b000; - else - rgb_led_color=3'b111; - end -end - // A bit useless since if the cpu ERORRS out or HALTS it will continue executing anyway //always @(HALT or ERROR or user_button) begin // if (HALT==1) begin @@ -68,15 +63,16 @@ end // end //end +// Create a 27 bit register +reg [26:0] counter = 0; -/*** RESET CIRCUIT ***/ - -reg [3:0] counter = 0; - +// Every positive edge increment register by 1 always @(posedge clk48) begin counter <= counter + 1; end +/*** RESET CIRCUIT ***/ + reg reset=0; reg [1:0] state=0; @@ -97,4 +93,113 @@ always @(posedge counter[3]) begin endcase end + + + +always @(negedge wr) begin + if(IOMEM==1'b1 && address_bus[7:0]==8'hA5 )begin + //disp_write_cache[disp_cache_end]<=data_bus_write[7:0]; + disp_write_cache[disp_cache_end]<=data_bus_write[15:8]; + disp_cache_end<=disp_cache_end+5'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; + else + rgb_led_color=3'b111; + end +end + +//------------------------------------------// +// Cache to allow the slow display to have a +// chance to keep up with the relentless CPU + +reg [4:0] disp_cache_start=0; +reg [4:0] disp_cache_end=0; +reg [7:0] disp_write_cache [31:0]; +reg ascii_state=0; +always @(posedge clk48)begin + case (ascii_state) + 1'b0:begin + if(ascii_data_ready&disp_cache_start!=disp_cache_end)begin + ascii_data<=disp_write_cache[disp_cache_start]; + disp_cache_start<=disp_cache_start+5'd1; + ascii_data_write_req<=1; + ascii_state<=1'b1; + end + end + 1'b1:begin + if(!ascii_data_ready)begin + ascii_data_write_req<=0; + ascii_state<=1'b0; + end + end + default: begin end + endcase +end + +wire I2C_SPEED=counter[7]; + +// Display driver + +wire ascii_data_ready; +reg ascii_data_write_req=0; +reg [7:0] ascii_data; +ascii_to_HD44780_driver LCD_DRIVER( + /* system */ + I2C_SPEED, + 1'b1, + + /* Data Input */ + ascii_data_ready, + ascii_data_write_req, + ascii_data, + + /* write circuitry */ + !pcf_busy, + pcf_write_req, + pcf_data, + pcf_command_data +); + +// Port expander driver + +wire pcf_write_req,pcf_command_data,pcf_busy; +wire [3:0]pcf_data; +wire [7:0]i2c_data; + +pcf8574_for_HD44780 PCF8574_driver( + .clock(I2C_SPEED), + + .pcf_write_req(pcf_write_req), + .pcf_command_data(pcf_command_data), + .pcf_data(pcf_data), + + .pcf_busy(pcf_busy), + .new_backlight(1'b0), + .backlight_update(1'b0), + + .I2C_BUSY(I2C_BUSY), + .I2C_SEND(I2C_SEND), + + .i2c_data(i2c_data) +); + +// I2C driver + +wire SCL,SDA,I2C_BUSY,I2C_SEND; +assign gpio_1=SCL; +assign gpio_0=SDA; +reg [7:0]i2c_data; +I2C_driver i2c_driver( + .clock(I2C_SPEED), + + .SDA_(SDA), + .SCL(SCL), + + .address(7'h27), + .I2C_BUSY(I2C_BUSY), + .I2C_SEND(I2C_SEND), + .i2c_data(i2c_data), +); + endmodule diff --git a/system/peripherals/I2C_driver.v b/system/peripherals/I2C_driver.v new file mode 100644 index 0000000..264caa8 --- /dev/null +++ b/system/peripherals/I2C_driver.v @@ -0,0 +1,258 @@ +/* I2C_driver.v - Implements an I2C interface + + This file is part of the 9086 project. + + Copyright (c) 2023 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 ( + input wire clock, + inout wire SDA_, + output reg SCL, + + input wire [6:0] address, + output reg I2C_BUSY=0, + input wire I2C_SEND, + input wire [7:0] i2c_data +); + +reg SDA; + +assign SDA_=(i2c_bus_dir==0)?SDA:1'bz; + +reg i2c_bus_dir=0; +reg [5:0] i2c_state = 6'b100100; + +reg [3:0] data_bit_counter; +reg [7:0] data_internal; +reg [6:0]address_internal; + +always @(posedge clock) begin + case (i2c_state) + /***** start sequence ******/ + 6'b000000:begin + i2c_bus_dir<=0; + SDA<=1; + SCL<=1; + i2c_state<=6'b000001; + end + 6'b000001:begin + SDA<=0; + SCL<=1; + i2c_state<=6'b000010; + end + 6'b000010:begin + SDA<=0; + SCL<=0; + i2c_state<=6'b000011; + data_bit_counter<=0; + end + /****** Set address ********/ + 6'b000011:begin + SCL<=0; + i2c_state<=6'b000100; + end + 6'b000100:begin + SCL<=0; + SDA<=address_internal[6:6]; + address_internal[6:0]<={address_internal[5:0],1'b0}; + data_bit_counter<=data_bit_counter+1; + i2c_state<=6'b000101; + end + 6'b000101:begin + SCL<=1; + i2c_state<=6'b000110; + end + 6'b000110:begin + SCL<=1; + if(data_bit_counter==4'd7) + i2c_state<=6'b000111; + else + i2c_state<=6'b000011; + end + /****** Read/Write *********/ + 6'b000111:begin + SCL<=0; + i2c_state<=6'b001000; + end + 6'b001000:begin + SCL<=0; + SDA<=0;/*Write=0 Read=1*/ + i2c_state<=6'b001001; + end + 6'b001001:begin + SCL<=1; + i2c_state<=6'b001010; + end + 6'b001010:begin + SCL<=1; + i2c_state<=6'b001011; + end + /****** Acknowledge ********/ + 6'b001011:begin + SCL<=0; + i2c_bus_dir<=1; + i2c_state<=6'b001100; + end + 6'b001100:begin + SCL<=0; + i2c_state<=6'b001101; + end + 6'b001101:begin + SCL<=1; + i2c_state<=6'b001110; + end + 6'b001110:begin + SCL<=1; + // if (SDA==1)begin + // led<=1; + // i2c_state<=6'b011111; + // end else begin + i2c_state<=6'b001111; + data_bit_counter<=0; + // end + end + /****** separator ********/ + 6'b001111:begin + SCL<=0; + SDA<=0; + i2c_state<=6'b010000; + end + 6'b010000:begin + SCL<=0; + i2c_state<=6'b010001; + end + 6'b010001:begin + SCL<=0; + SDA<=1; + i2c_state<=6'b010010; + end + 6'b010010:begin + SCL<=0; + SDA<=1; + i2c_state<=6'b010011; + end + 6'b010011:begin + SCL<=0; + SDA<=0; + i2c_state<=6'b010100; + end + /****** Send data ********/ + 6'b010100:begin + SCL<=0; + i2c_bus_dir<=0; + 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}; + data_bit_counter<=data_bit_counter+1; + i2c_state<=6'b010110; + end + 6'b010110:begin + SCL<=1; + i2c_state<=6'b010111; + end + 6'b010111:begin + SCL<=1; + if(data_bit_counter==4'd8)begin + i2c_state<=6'b011000; + end else + i2c_state<=6'b010100; + end + /****** Acknowledge ********/ + 6'b011000:begin + i2c_bus_dir<=1; + SCL<=0; + i2c_state<=6'b011001; + end + 6'b011001:begin + SCL<=0; + i2c_state<=6'b011010; + end + 6'b011010:begin + SCL<=1; + i2c_state<=6'b011011; + end + 6'b011011:begin + SCL<=1; + // if (SDA==1)begin + // led<=1; + // i2c_state<=6'b011111; + // end else begin + i2c_state<=6'b011100; + i2c_bus_dir<=0; + // end + end + /****** separator ********/ + 6'b011100:begin + SCL<=0; + SDA<=0; + i2c_state<=6'b011101; + end + 6'b011101:begin + SCL<=0; + i2c_state<=6'b011110; + end + 6'b011110:begin + SCL<=0; + SDA<=1; + i2c_state<=6'b011111; + end + 6'b011111:begin + SCL<=0; + SDA<=1; + i2c_state<=6'b100000; + end + 6'b100000:begin + SCL<=0; + SDA<=0; + i2c_state<=6'b100001; + end + /****** stop bit *******/ + 6'b100001:begin + SCL<=1; + SDA<=0; + i2c_state<=6'b100010; + end + 6'b100010:begin + SCL<=1; + SDA<=1; + i2c_state<=6'b100011; + end + 6'b100011:begin + SCL<=1; + SDA<=1; + i2c_state<=6'b100100; + I2C_BUSY<=0; + end + 6'b100100:begin + if(I2C_SEND==1)begin + I2C_BUSY<=1; + i2c_state<=0; + data_internal<=i2c_data; + address_internal<=address; + end + end + default:begin + SCL<=0; + SDA<=0; + end + + endcase +end + +endmodule diff --git a/system/peripherals/ascii_to_HD44780_driver.v b/system/peripherals/ascii_to_HD44780_driver.v new file mode 100644 index 0000000..fbf40d9 --- /dev/null +++ b/system/peripherals/ascii_to_HD44780_driver.v @@ -0,0 +1,114 @@ +/* ascii_to_HD44780_driver.v - driver that takes in ascii and outputs control + sequences for an HD44780 display + + This file is part of the 9086 project. + + Copyright (c) 2023 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 ascii_to_HD44780_driver ( + /* system */ + input wire clock, + input wire rst_n, + + /* Data Input */ + output reg in_data_ready=0, + input data_write_req, + input [7:0] in_ascii_data, + + /* write circuitry */ + input wire done_writing, + output reg write_req=0, + output reg [3:0] data, + output reg cmd_data=0 +); + +initial begin + init_cmd_data[ 0] = 4'h3; + init_cmd_data[ 1] = 4'h3; + init_cmd_data[ 2] = 4'h3; + init_cmd_data[ 3] = 4'h2; + init_cmd_data[ 4] = 4'h2; //0x28 + init_cmd_data[ 5] = 4'h8; + init_cmd_data[ 6] = 4'h0; //0x07 + init_cmd_data[ 7] = 4'h7; + init_cmd_data[ 8] = 4'h0; //0x0C + init_cmd_data[ 9] = 4'hC; + init_cmd_data[10] = 4'h0; //0x01 + init_cmd_data[11] = 4'h1; + init_cmd_data[12] = 4'h0; //0x06 + init_cmd_data[13] = 4'h6; + init_cmd_data[14] = 4'h0; // 0x02 + init_cmd_data[15] = 4'h2; +end + +reg [4:0] init_seq=0; +reg [5:0] data_write_wait_counter = 0; + +reg [3:0] init_cmd_data [15:0]; + +reg byte_n=0; + +reg data_write_latch=0; +reg [7:0]print_data; + +always @(posedge clock,negedge rst_n) begin + if(rst_n==0)begin + in_data_ready<=0; + write_req<=1'b0; + init_seq<=5'd0; + data_write_wait_counter<=6'd0; + byte_n<=1'b0; + cmd_data<=1'b0; + end else if(done_writing)begin + if(init_seq!=5'd16&&data_write_wait_counter==6'd0)begin + data<=init_cmd_data[init_seq[3:0]]; + init_seq<=init_seq+5'd1; + write_req<=1'b1; + data_write_wait_counter<=6'd50; + in_data_ready<=0; + end else if (data_write_wait_counter!=6'd0) begin + data_write_wait_counter<=data_write_wait_counter-6'd1; + write_req<=1'b0; + in_data_ready<=0; + end else if(data_write_req==1&&data_write_latch==0)begin + data_write_latch<=1; + print_data<=in_ascii_data; + in_data_ready<=0; + byte_n<=0; + end else if(data_write_latch&byte_n==0)begin + data<=print_data[7:4]; + write_req<=1'b1; + data_write_wait_counter<=6'd50; + byte_n<=1; + end else if(data_write_latch&byte_n==1)begin + data<=print_data[3:0]; + write_req<=1'b1; + data_write_wait_counter<=6'd50; + byte_n<=0; + data_write_latch<=0; + end else begin + cmd_data<=1; + in_data_ready<=1; + end + end else begin + write_req<=1'b0; + in_data_ready<=0; + end +end + + + +endmodule diff --git a/system/peripherals/pcf8574_for_HD44780.v b/system/peripherals/pcf8574_for_HD44780.v new file mode 100644 index 0000000..cb93706 --- /dev/null +++ b/system/peripherals/pcf8574_for_HD44780.v @@ -0,0 +1,123 @@ +/* pcf8574_for_HD44780.v - takes in control sequences for an HD44780 display + and outputs data that control a pcf8574 connected to a HD44780 in a standard + manner over i2c + + This file is part of the 9086 project. + + Copyright (c) 2023 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 pcf8574_for_HD44780( +input wire clock, + +input wire pcf_write_req, +input wire pcf_command_data, +input wire [3:0]pcf_data, +output reg pcf_busy=0, +input new_backlight, +input backlight_update, + +input I2C_BUSY, +output reg I2C_SEND=0, + +output reg [7:0]i2c_data +); + +reg backlight=1; +reg backlight_latch=0; +reg [3:0]pcf_state=0; + +always @(posedge clock) begin +// [ D7 , D6 , D5 , D4 , BACKLIGHT , EN , WRITE , COMMAND/DATA ] + if(backlight_update) + backlight_latch<=1; + case(pcf_state) + 4'b0000: begin /*Idle*/ + if(pcf_write_req&&I2C_BUSY==0)begin + pcf_state<=4'b0001; + I2C_SEND<=0; + pcf_busy<=1; + end else if(backlight_latch&&I2C_BUSY==0)begin + pcf_state<=4'b1111; + I2C_SEND<=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; + backlight_latch<=0; + pcf_state<=4'b0010; + end + end + 4'b0010:begin + if(I2C_BUSY==1)begin + I2C_SEND<=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; + backlight_latch<=0; + pcf_state<=4'b0100; + end + end + 4'b0100:begin + if(I2C_BUSY==1)begin + I2C_SEND<=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; + pcf_state<=4'b0110; + end + end + 4'b0110: begin + if(I2C_BUSY==1)begin + I2C_SEND<=0; + pcf_state<=4'b0111; + end + end + 4'b0111:begin + if(I2C_BUSY==0)begin + pcf_state<=4'b0000; + pcf_busy<=1'b0; + end + end + 4'b1111:begin + if(I2C_BUSY==1)begin + pcf_state<=4'b0000; + I2C_SEND<=1'b0; + pcf_busy<=1'b0; + end else begin + i2c_data<={4'b0,backlight,1'b0,1'b0,1'b0}; + backlight_latch<=0; + I2C_SEND<=1'b1; + pcf_busy<=1'b1; + end + end + default:begin + pcf_state<=0; + end + endcase +end + +endmodule