280 lines
6.3 KiB
Verilog
280 lines
6.3 KiB
Verilog
/* top.v - Implements FPGA and Board specific circuitry
|
|
|
|
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/>. */
|
|
|
|
`include "error_header.v"
|
|
|
|
module fpga_top(
|
|
input clk48,
|
|
|
|
input user_button,
|
|
// output reset_n,
|
|
|
|
output rgb_led0_r,
|
|
output rgb_led0_g,
|
|
output rgb_led0_b,
|
|
|
|
inout gpio_0,/*sda*/
|
|
output gpio_1 /*scl*/
|
|
);
|
|
|
|
wire HALT;
|
|
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;
|
|
|
|
wire CPU_SPEED=counter[6];
|
|
|
|
system system(
|
|
/* MISC */ CPU_SPEED,reset
|
|
/* MEMORY / IO */ ,address_bus,data_bus_read,data_bus_write,BHE,rd,wr,IOMEM,HALT,ERROR
|
|
);
|
|
|
|
reg [2:0]rgb_led_color;
|
|
assign rgb_led0_r=rgb_led_color[0];
|
|
assign rgb_led0_g=rgb_led_color[1];
|
|
assign rgb_led0_b=rgb_led_color[2];
|
|
|
|
// 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
|
|
// /* yellow */
|
|
// rgb_led_color<=3'b100;
|
|
// end else if (ERROR != `ERROR_BITS'b0) begin
|
|
// /* red */
|
|
// rgb_led_color<=3'b110;
|
|
// end else begin
|
|
// /* green */
|
|
// rgb_led_color<=3'b101;
|
|
// end
|
|
//end
|
|
|
|
// Create a 27 bit register
|
|
reg [26: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;
|
|
|
|
always @(posedge counter[15]) begin
|
|
if(user_button==0)
|
|
state=2'b00;
|
|
case (state)
|
|
2'b00:begin
|
|
reset<=0;
|
|
state<=2'b01;
|
|
end
|
|
2'b01:begin
|
|
reset<=1;
|
|
state<=2'b10;
|
|
end
|
|
default: begin
|
|
end
|
|
endcase
|
|
end
|
|
|
|
/* TODO This seems to be a bug with YOSYS. If I don't initialise the memory
|
|
* it doesn't work (even though it does in sim), and for example if i
|
|
* initialise 1 twice to the same value it breaks again.
|
|
*
|
|
* This seems to affect brainfuck_compiled.asm and specifically when the
|
|
* compiled code starts running ( or the compiler i guess )
|
|
*/
|
|
initial begin
|
|
disp_write_cache[ 0]=8'h46;
|
|
disp_write_cache[ 1]=8'h46;
|
|
disp_write_cache[ 2]=8'h46;
|
|
disp_write_cache[ 3]=8'h46;
|
|
disp_write_cache[ 4]=8'h46;
|
|
disp_write_cache[ 5]=8'h46;
|
|
disp_write_cache[ 6]=8'h46;
|
|
disp_write_cache[ 7]=8'h46;
|
|
disp_write_cache[ 8]=8'h46;
|
|
disp_write_cache[ 9]=8'h46;
|
|
disp_write_cache[10]=8'h46;
|
|
disp_write_cache[11]=8'h46;
|
|
disp_write_cache[12]=8'h46;
|
|
disp_write_cache[13]=8'h46;
|
|
disp_write_cache[14]=8'h46;
|
|
disp_write_cache[15]=8'h46;
|
|
disp_write_cache[16]=8'h46;
|
|
disp_write_cache[17]=8'h46;
|
|
disp_write_cache[18]=8'h46;
|
|
disp_write_cache[19]=8'h46;
|
|
disp_write_cache[20]=8'h46;
|
|
disp_write_cache[21]=8'h46;
|
|
disp_write_cache[22]=8'h46;
|
|
disp_write_cache[23]=8'h46;
|
|
disp_write_cache[24]=8'h46;
|
|
disp_write_cache[25]=8'h46;
|
|
disp_write_cache[26]=8'h46;
|
|
disp_write_cache[27]=8'h46;
|
|
disp_write_cache[28]=8'h46;
|
|
disp_write_cache[29]=8'h46;
|
|
disp_write_cache[30]=8'h46;
|
|
disp_write_cache[31]=8'h46;
|
|
disp_write_cache[32]=8'h46;
|
|
disp_write_cache[33]=8'h46;
|
|
disp_write_cache[34]=8'h46;
|
|
disp_write_cache[35]=8'h46;
|
|
disp_write_cache[36]=8'h46;
|
|
disp_write_cache[37]=8'h46;
|
|
disp_write_cache[38]=8'h46;
|
|
disp_write_cache[39]=8'h46;
|
|
disp_write_cache[40]=8'h46;
|
|
disp_write_cache[41]=8'h46;
|
|
disp_write_cache[42]=8'h46;
|
|
disp_write_cache[43]=8'h46;
|
|
disp_write_cache[44]=8'h46;
|
|
disp_write_cache[45]=8'h46;
|
|
disp_write_cache[46]=8'h46;
|
|
disp_write_cache[47]=8'h46;
|
|
disp_write_cache[48]=8'h46;
|
|
disp_write_cache[49]=8'h46;
|
|
disp_write_cache[50]=8'h46;
|
|
disp_write_cache[51]=8'h46;
|
|
disp_write_cache[52]=8'h46;
|
|
disp_write_cache[53]=8'h46;
|
|
disp_write_cache[54]=8'h46;
|
|
disp_write_cache[55]=8'h46;
|
|
disp_write_cache[56]=8'h46;
|
|
disp_write_cache[57]=8'h46;
|
|
disp_write_cache[58]=8'h46;
|
|
disp_write_cache[59]=8'h46;
|
|
disp_write_cache[60]=8'h46;
|
|
disp_write_cache[61]=8'h46;
|
|
disp_write_cache[62]=8'h46;
|
|
disp_write_cache[63]=8'h46;
|
|
end
|
|
|
|
//TODO similarly as above, if I remove this the same thing breaks
|
|
always @(negedge wr) begin
|
|
end
|
|
|
|
|
|
//------------------------------------------//
|
|
// Cache to allow the slow display to have a
|
|
// chance to keep up with the relentless CPU
|
|
|
|
reg [5:0] disp_cache_start=0;
|
|
reg [5:0] disp_cache_end=0;
|
|
reg [7:0] disp_write_cache [63: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+6'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 else if(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+6'd1;
|
|
ascii_data_write_req<=1;
|
|
ascii_state<=1'b1;
|
|
end
|
|
end
|
|
|
|
if(ascii_state==1'b1)begin
|
|
if(!ascii_data_ready)begin
|
|
ascii_data_write_req<=0;
|
|
ascii_state<=1'b0;
|
|
end
|
|
end
|
|
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;
|
|
|
|
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
|