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