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!

This commit is contained in:
(Tim) Efthimis Kritikos 2023-11-09 22:10:34 +00:00
parent e0dc7bae07
commit a88c420ca5
5 changed files with 617 additions and 15 deletions

View File

@ -69,10 +69,12 @@ endif
ECP5_TARGETS=synth_ecp5.json synth_ecp5_out.config synth_ecp5.bit synth_ecp5.dfu ECP5_TARGETS=synth_ecp5.json synth_ecp5_out.config synth_ecp5.bit synth_ecp5.dfu
ECP5_TARGETS+=abc.history # created from yosys 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... #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} ${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 synth_ecp5_out.config:synth_ecp5.json
${QUIET_NEXTPNR} ${QUIET_NEXTPNR}

View File

@ -28,6 +28,9 @@ module fpga_top(
output rgb_led0_r, output rgb_led0_r,
output rgb_led0_g, output rgb_led0_g,
output rgb_led0_b, output rgb_led0_b,
inout gpio_0,/*sda*/
output gpio_1 /*scl*/
); );
wire HALT; wire HALT;
@ -35,6 +38,7 @@ wire [`ERROR_BITS-1:0]ERROR;
wire [19:0] address_bus; wire [19:0] address_bus;
wire [15:0] data_bus_read,data_bus_write; wire [15:0] data_bus_read,data_bus_write;
wire rd,wr,BHE,IOMEM; wire rd,wr,BHE,IOMEM;
system system( system system(
/* MISC */ clk48,reset /* MISC */ clk48,reset
/* MEMORY / IO */ ,address_bus,data_bus_read,data_bus_write,BHE,rd,wr,IOMEM,HALT,ERROR /* 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_g=rgb_led_color[1];
assign rgb_led0_b=rgb_led_color[2]; 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 // A bit useless since if the cpu ERORRS out or HALTS it will continue executing anyway
//always @(HALT or ERROR or user_button) begin //always @(HALT or ERROR or user_button) begin
// if (HALT==1) begin // if (HALT==1) begin
@ -68,15 +63,16 @@ end
// end // end
//end //end
// Create a 27 bit register
reg [26:0] counter = 0;
/*** RESET CIRCUIT ***/ // Every positive edge increment register by 1
reg [3:0] counter = 0;
always @(posedge clk48) begin always @(posedge clk48) begin
counter <= counter + 1; counter <= counter + 1;
end end
/*** RESET CIRCUIT ***/
reg reset=0; reg reset=0;
reg [1:0] state=0; reg [1:0] state=0;
@ -97,4 +93,113 @@ always @(posedge counter[3]) begin
endcase endcase
end 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 endmodule

View File

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

View File

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

View File

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