Resolve "Organize Project Better"

This commit is contained in:
Byron Lathi
2023-09-19 02:57:26 +00:00
parent a770d938de
commit c466c62969
18 changed files with 132 additions and 85 deletions

View File

@@ -0,0 +1,24 @@
module addr_decode
(
input [15:0] i_addr,
output o_rom_cs,
output o_leds_cs,
output o_timer_cs,
output o_multiplier_cs,
output o_divider_cs,
output o_uart_cs,
output o_spi_cs,
output o_sdram_cs
);
assign o_rom_cs = i_addr >= 16'hf000 && i_addr <= 16'hffff;
assign o_timer_cs = i_addr >= 16'heff8 && i_addr <= 16'heffb;
assign o_multiplier_cs = i_addr >= 16'heff0 && i_addr <= 16'heff7;
assign o_divider_cs = i_addr >= 16'hefe8 && i_addr <= 16'hefef;
assign o_uart_cs = i_addr >= 16'hefe6 && i_addr <= 16'hefe7;
assign o_spi_cs = i_addr >= 16'hefd8 && i_addr <= 16'hefdb;
assign o_leds_cs = i_addr == 16'hefff;
assign o_sdram_cs = i_addr < 16'he000;
endmodule

106
hw/efinix_fpga/src/crc7.sv Normal file
View File

@@ -0,0 +1,106 @@
module crc7 #(parameter POLYNOMIAL = 8'h89)
(
input clk,
input rst,
input load,
input [39:0] data_in,
output logic [6:0] crc_out,
output logic valid
);
logic [46:0] data;
logic [46:0] next_data;
logic [46:0] polyshift;
typedef enum bit [1:0] {IDLE, WORKING, VALID} macro_t;
struct packed {
macro_t macro;
logic [5:0] count;
} state, next_state;
always_ff @(posedge clk) begin
if (rst) begin
polyshift <= {POLYNOMIAL, 39'b0}; //start all the way at the left
data <= '0;
state.macro <= IDLE;
state.count <= '0;
end else begin
if (load) begin
data <= {data_in, 7'b0};
end else begin
data <= next_data;
end
state <= next_state;
if (state.macro == WORKING) begin
polyshift <= polyshift >> 1;
end
if (state.macro == VALID) begin
polyshift <= {POLYNOMIAL, 39'b0};
end
end
end
always_comb begin
next_state = state;
case (state.macro)
IDLE: begin
if (load) begin
next_state.macro = WORKING;
next_state.count = '0;
end
end
WORKING: begin
if (state.count < 39) begin
next_state.count = state.count + 6'b1;
end else begin
next_state.macro = VALID;
next_state.count = '0;
end
end
VALID: begin // Same as IDLE, but IDLE is just for reset.
if (load) begin
next_state.macro = WORKING;
next_state.count = '0;
end
end
default:;
endcase
end
always_comb begin
valid = 0;
next_data = '0;
crc_out = '0;
case (state.macro)
IDLE: begin
valid = 0;
end
WORKING: begin
if (data[6'd46 - state.count]) begin
next_data = data ^ polyshift;
end else begin
next_data = data;
end
end
VALID: begin
valid = ~load;
next_data = data;
crc_out = data[6:0];
end
default:;
endcase
end
endmodule

View File

@@ -0,0 +1,92 @@
module divider_wrapper(
input clk,
input divclk,
input reset,
input [7:0] i_data,
output logic [7:0] o_data,
input cs,
input rwb,
input [2:0] addr
);
logic [15:0] numer, denom;
logic [15:0] quotient, remain;
logic [15:0] r_quotient, r_remain;
logic clken, rfd;
assign clken = '1;
divider u_divider(
.numer ( numer ),
.denom ( denom ),
.clken ( clken ),
.clk ( divclk ),
.reset ( reset ),
.quotient ( quotient ),
.remain ( remain ),
.rfd ( rfd )
);
always_ff @(negedge clk) begin
if (reset) begin
numer <= '0;
denom <= '0;
end
if (cs & ~rwb) begin
case (addr)
3'h0: begin
numer[7:0] <= i_data;
end
3'h1: begin
numer[15:8] <= i_data;
end
3'h2: begin
denom[7:0] <= i_data;
end
3'h3: begin
denom[15:8] <= i_data;
end
endcase
end
end
always_ff @(posedge divclk) begin
if (rfd) begin
r_quotient <= quotient;
r_remain <= remain;
end
end
always_comb begin
case (addr)
3'h4: begin
o_data = r_quotient[7:0];
end
3'h5: begin
o_data = r_quotient[15:8];
end
3'h6: begin
o_data = r_remain[7:0];
end
3'h7: begin
o_data = r_remain[15:8];
end
endcase
end
endmodule

View File

@@ -0,0 +1,30 @@
module interrupt_controller
(
input clk,
input reset,
input [7:0] i_data,
output logic [7:0] o_data,
input cs,
input rwb,
output logic irqb_master,
input irqb0, irqb1, irqb2, irqb3,
input irqb4, irqb5, irqb6, irqb7
);
//All of the inputs are low level triggered.
logic [7:0] irqbv;
assign irqbv = {irqb0, irqb1, irqb2, irqb3, irqb4, irqb5, irqb6, irqb7};
always @(posedge clk) begin
o_data <= irqbv;
irqb_master = &irqbv;
if (cs & ~rwb) begin
o_data <= o_data | i_data;
end
end
endmodule

View File

@@ -0,0 +1,24 @@
module leds
(
input clk,
input [7:0] i_data,
output logic [7:0] o_data,
input cs,
input rwb,
output logic [7:0] o_leds
);
logic [7:0] _data;
assign o_leds = ~_data;
assign o_data = _data;
always @(negedge clk) begin
if (~rwb & cs) begin
_data <= i_data;
end
end
endmodule

View File

@@ -0,0 +1,46 @@
module multiplier(
input clk,
input reset,
input [7:0] i_data,
output logic [7:0] o_data,
input cs,
input rwb,
input [2:0] addr
);
logic [15:0] a, b;
logic [31:0] out;
always_ff @(negedge clk) begin
if (reset) begin
a <= '0;
b <= '0;
end
if (cs & ~rwb) begin
case (addr)
3'h0: begin
a[7:0] <= i_data;
end
3'h1: begin
a[15:8] <= i_data;
end
3'h2: begin
b[7:0] <= i_data;
end
3'h3: begin
b[15:8] <= i_data;
end
endcase
end
end
assign out = a * b;
assign o_data = out[((addr-4)*8)+:8];
endmodule

49
hw/efinix_fpga/src/rom.sv Normal file
View File

@@ -0,0 +1,49 @@
/////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2013-2018 Efinix Inc. All rights reserved.
//
// Single Port ROM
//
// *******************************
// Revisions:
// 0.0 Initial rev
// 1.0 Finalized RTL macro
// *******************************
module rom
#(
parameter DATA_WIDTH = 16,
parameter ADDR_WIDTH = 8,
parameter OUTPUT_REG = "FALSE",
parameter RAM_INIT_FILE = "init_hex.mem"
)
(
input [ADDR_WIDTH-1:0] addr,
input clk,
output [DATA_WIDTH-1:0] data
);
localparam MEMORY_DEPTH = 2**ADDR_WIDTH;
reg [DATA_WIDTH-1:0]r_rdata_1P;
reg [DATA_WIDTH-1:0]r_rdata_2P;
reg [DATA_WIDTH-1:0] rom[MEMORY_DEPTH-1:0];
initial begin
$readmemh(RAM_INIT_FILE, rom);
end
always@(posedge clk)
begin
r_rdata_1P <= rom[addr];
r_rdata_2P <= r_rdata_1P;
end
generate
if (OUTPUT_REG == "TRUE")
assign data = r_rdata_2P;
else
assign data = r_rdata_1P;
endgenerate
endmodule

View File

@@ -0,0 +1,235 @@
module sd_controller(
input clk,
input sd_clk,
input rst,
input [2:0] addr,
input [7:0] data,
input cs,
input rw,
input i_sd_cmd,
output logic o_sd_cmd,
input i_sd_data,
output logic o_sd_data,
output logic [7:0] data_out
);
logic [31:0] arg;
logic [5:0] cmd;
logic [47:0] rxcmd_buf;
logic [31:0] rx_val;
logic [7:0] rxdata_buf [512];
logic [8:0] data_count;
logic [15:0] data_crc;
assign rx_val = rxcmd_buf[39:8];
always_comb begin
data_out = 'x;
if (addr < 4'h4) begin
data_out = rx_val[8 * addr +: 8];
end else if (addr == 4'h4) begin
data_out = {data_flag, read_flag};
end else if (addr == 4'h5) begin
data_out = rxdata_buf[data_count];
end
end
logic read_flag, next_read_flag;
logic data_flag, next_data_flag;
typedef enum bit [2:0] {IDLE, LOAD, CRC, TXCMD, RXCMD, TXDATA, RXDATA, RXDCRC} macro_t;
struct packed {
macro_t macro;
logic [8:0] count;
logic [2:0] d_bit_count;
} state, next_state;
always_ff @(negedge clk) begin
if (rst) begin
state.macro <= IDLE;
state.count <= '0;
state.d_bit_count <= '1;
read_flag <= '0;
data_flag <= '0;
data_count <= '0;
end else begin
if (state.macro == TXCMD || state.macro == CRC) begin
if (sd_clk) begin
state <= next_state;
end
end else if (state.macro == RXCMD || state.macro == RXDATA || state.macro == RXDCRC) begin
if (~sd_clk) begin
state <= next_state;
end
end else begin
state <= next_state;
end
end
if (sd_clk) begin
read_flag <= next_read_flag;
data_flag <= next_data_flag;
end
if (cs & ~rw) begin
if (addr < 4'h4) begin
arg[8 * addr +: 8] <= data;
end else if (addr == 4'h4) begin
cmd <= data[6:0];
end
end
if (cs & addr == 4'h5 && sd_clk) begin
data_count <= data_count + 8'b1;
end
if (state.macro == RXCMD) begin
rxcmd_buf[6'd46-state.count] <= i_sd_cmd; //we probabily missed bit 47
end
if (state.macro == RXDATA && ~sd_clk) begin
rxdata_buf[state.count][state.d_bit_count] <= i_sd_data;
end
if (state.macro == RXDCRC && ~sd_clk) begin
data_crc[4'd15-state.count] <= i_sd_data;
data_count <= '0;
end
end
logic [6:0] crc;
logic load_crc;
logic crc_valid;
logic [39:0] _packet;
assign _packet = {1'b0, 1'b1, cmd, arg};
logic [47:0] packet_crc;
assign packet_crc = {_packet, crc, 1'b1};
crc7 u_crc7(
.clk(clk),
.rst(rst),
.load(load_crc),
.data_in(_packet),
.crc_out(crc),
.valid(crc_valid)
);
always_comb begin
next_state = state;
next_read_flag = read_flag;
next_data_flag = data_flag;
case (state.macro)
IDLE: begin
if (~i_sd_cmd) begin // receive data if sd pulls cmd low
next_state.macro = RXCMD;
end
if (~i_sd_data) begin
next_state.d_bit_count = '1;
next_state.macro = RXDATA;
end
if (addr == 4'h4 & cs & ~rw) begin // transmit if cpu writes to cmd
next_state.macro = LOAD;
end
if (addr == 4'h4 & cs & rw) begin
next_read_flag = '0;
end
if (addr == 4'h5 & cs) begin
next_data_flag = '0;
end
end
LOAD: begin
next_state.macro = CRC;
end
CRC: begin
next_state.macro = TXCMD;
end
TXCMD: begin
if (state.count < 47) begin
next_state.count = state.count + 6'b1;
end else begin
next_state.macro = IDLE;
next_state.count = '0;
end
end
RXCMD: begin
if (state.count < 47) begin
next_state.count = state.count + 6'b1;
end else begin
next_read_flag = '1;
next_state.macro = IDLE;
next_state.count = '0;
end
end
RXDATA: begin
if (state.count < 511 || (state.count == 511 && state.d_bit_count > 0)) begin
if (state.d_bit_count == 8'h0) begin
next_state.count = state.count + 9'b1;
end
next_state.d_bit_count = state.d_bit_count - 3'h1;
end else begin
next_data_flag = '1;
next_state.macro = RXDCRC;
next_state.count = '0;
end
end
RXDCRC: begin
if (state.count < 16) begin
next_state.count = state.count + 9'b1;
end else begin
next_state.macro = IDLE;
next_state.count = '0;
end
end
default: begin
next_state.macro = IDLE;
next_state.count = '0;
end
endcase
end
always_comb begin
o_sd_cmd = '1; //default to 1
o_sd_data = '1;
load_crc = '0;
case (state.macro)
IDLE:;
CRC: begin
load_crc = '1;
end
TXCMD: begin
o_sd_cmd = packet_crc[6'd47 - state.count];
end
RXCMD:;
default:;
endcase
end
endmodule

View File

@@ -0,0 +1,303 @@
module sdram_adapter(
input logic i_cpuclk,
input logic i_arst, // Async Reset
input logic i_sysclk, // Controller Clock (100MHz)
input logic i_sdrclk, // t_su and t_wd clock (200MHz)
input logic i_tACclk, // t_ac clock (200MHz)
input logic i_cs, // Chip select
input logic i_rwb, // Read/Write. Write is low
input logic [24:0] i_addr, // Input address. Byte addressed
input logic [7:0] i_data, // Input Data
output logic [7:0] o_data, // Output data
output o_sdr_init_done,
output o_wait,
output o_sdr_CKE,
output o_sdr_n_CS,
output o_sdr_n_RAS,
output o_sdr_n_CAS,
output o_sdr_n_WE,
output [1:0] o_sdr_BA,
output [12:0] o_sdr_ADDR,
output [15:0] o_sdr_DATA,
output [15:0] o_sdr_DATA_oe,
input [15:0] i_sdr_DATA,
output [1:0] o_sdr_DQM
);
logic [1:0] w_sdr_CKE;
logic [1:0] w_sdr_n_CS;
logic [1:0] w_sdr_n_RAS;
logic [1:0] w_sdr_n_CAS;
logic [1:0] w_sdr_n_WE;
logic [3:0] w_sdr_BA;
logic [25:0] w_sdr_ADDR;
logic [31:0] w_sdr_DATA;
logic [31:0] w_sdr_DATA_oe;
logic [3:0] w_sdr_DQM;
assign o_sdr_CKE = w_sdr_CKE[0]; //Using SOFT ddio, ignore second cycle
assign o_sdr_n_CS = w_sdr_n_CS[0];
assign o_sdr_n_RAS = w_sdr_n_RAS[0];
assign o_sdr_n_CAS = w_sdr_n_CAS[0];
assign o_sdr_n_WE = w_sdr_n_WE[0];
assign o_sdr_BA = w_sdr_BA[0+:2];
assign o_sdr_ADDR = w_sdr_ADDR[0+:13];
assign o_sdr_DATA = w_sdr_DATA[0+:16];
assign o_sdr_DATA_oe = w_sdr_DATA_oe[0+:16];
assign o_sdr_DQM = w_sdr_DQM[0+:2];
// What should happen when the cpu writes something?
// 1. Address should already be calculated from the memory mapper, don't need to worry about it
// 2. Data byte position needs to be determined. Each write is 32 bits, so the dm bits need to
// be set and the byte shifted to the correct position
// 3. write enable and last should be set high. Only ever do bursts of 1.
// 4. Sample wr_ack and when it goes high, release write_enable and last
// What should happen when the cpu reads something?
// 1. Address should already be calculated from the memory mapper, don't need to worry about it
// 2. read_enable and last should be set high. Only ever to bursts of 1.
// 3. Sample rd_ack and when it goes high, release read_enable and last
// 4. Sample read_valid signal. When it is high, grab the data on the on the bus.
// The returned data will be 16 bit, so you need to extract the correct byte. (or will it be 32?)
// when writing, the write data is only valid on a falling edge.
// Really all of this should be done on falling edges.
// But basically if we are in access, and cpuclk goes low, go back to wait.
// If something actually happened, we would be in one of the read/write states.
enum bit [1:0] {ACCESS, READ_WAIT, WRITE_WAIT, WAIT} state, next_state;
logic w_read, w_write, w_last;
logic [23:0] w_addr, r_addr;
logic [31:0] w_data_i, w_data_o;
logic [3:0] w_dm, r_dm;
logic w_wr_ack, w_rd_ack, w_rd_valid;
logic [7:0] data, _data;
logic w_data_valid;
logic [31:0] r_write_data;
logic [1:0] counter, next_counter;
always @(posedge i_sysclk) begin
if (i_arst) begin
state <= WAIT;
counter <= '0;
end else begin
state <= next_state;
counter <= next_counter;
r_write_data <= w_data_i;
r_addr <= w_addr;
r_dm <= w_dm;
end
if (w_data_valid)
o_data <= _data;
end
logic r_wait;
logic _r_wait;
assign o_wait = r_wait;
// we need to assert rdy low until a falling edge if a reset happens
always @(posedge i_sysclk or posedge i_arst) begin
if (i_arst == '1) begin
r_wait <= '0;
_r_wait <= '0;
end else begin
if (o_dbg_ref_req) begin
r_wait <= '1;
end else if (i_cpuclk == '1) begin
_r_wait <= '1;
end
if (i_cpuclk == '0) begin
if (_r_wait) begin
_r_wait <= '0;
r_wait <= '0;
end
end
end
end
//because of timing issues, We really need to trigger
//the write on the falling edge of phi2. There is a 2ns
//delay between the rising edge of phi2 and data valid
//Since the address is valid on the previous falling edge,
//Reads can occur on the rising edge I guess.
//so basically cpu clock goes high when cs goes high we go into a priming state
//where we wait until cs goes low. when cpu clock is low, do the actual write.
//in terms of the existing state, the access state needs to only do something
//if selected AND cpu_clock is low. If cpu clock is high, we should be in wait,
//and after the read/write is complete we should also go back to wait.
//actually that may only apply to writes, since reads should occur at the rising
//edge of i_cpuclk
//Starts out in state 0 with cpuclk low and cs high.
//Then, cpuclk goes high. This is now a valid time to read
//After this, cpuclk goes low again, this is now a valid time to write.
//so basically if cpuclk goes low when cs is low, go to wait state
//what I am thinking is basically 2 states like before. wait and access.
//we go to access when cpuclk is high and cs is high.
//we can read as soon as we want if rwn is high.
//BUT if rwb is low then we have to wait untl cpuclk goes low again.
always_comb begin
next_state = state;
next_counter = counter;
w_addr = '0;
w_dm = '0;
w_read = '0;
w_write = '0;
w_last = '0;
w_data_i = '0;
w_data_valid = '0;
_data = 0;
unique case (state)
WAIT: begin
if (i_cs & i_cpuclk)
next_state = ACCESS;
end
ACCESS: begin
// only do something if selected
if (i_cs) begin
w_addr = {{i_addr[24:2]}, {1'b0}};; // divide by 2, set last bit to 0
if (i_rwb) begin //read
w_read = '1;
w_last = '1;
// dm is not needed for reads?
if (w_rd_ack) next_state = READ_WAIT;
end else begin //write
w_data_i = i_data << (8*i_addr[1:0]);
//w_data_i = {4{i_data}}; //does anything get through?
w_dm = ~(4'b1 << i_addr[1:0]);
if (~i_cpuclk) begin
w_write = '1;
w_last = '1;
next_state = WRITE_WAIT;
end
end
end
end
WRITE_WAIT: begin
// stay in this state until write is acknowledged.
w_write = '1;
w_last = '1;
w_data_i = r_write_data;
w_dm = r_dm;
w_addr = r_addr;
if (w_wr_ack) next_state = WAIT;
end
READ_WAIT: begin
if (w_rd_valid) begin
w_data_valid = '1;
_data = w_data_o[8*i_addr[1:0]+:8];
end
// you must wait until the next cycle!
if (~i_cpuclk) begin
next_state = WAIT;
end
end
endcase
end
//this seems scuffed
logic [23:0] addr_mux_out;
always_comb begin
if (state == ACCESS) begin
addr_mux_out = w_addr;
end else begin
addr_mux_out = r_addr;
end
end
logic o_dbg_tRTW_done;
logic o_dbg_ref_req;
logic o_dbg_wr_ack;
logic o_dbg_rd_ack;
logic [1:0] o_dbg_n_CS;
logic [1:0] o_dbg_n_RAS;
logic [1:0] o_dbg_n_CAS;
logic [1:0] o_dbg_n_WE;
logic [3:0] o_dbg_BA;
logic [25:0] o_dbg_ADDR;
logic [31:0] o_dbg_DATA_out;
logic [31:0] o_dbg_DATA_in;
logic o_sdr_init_done;
logic [3:0] o_sdr_state;
assign o_ref_req = o_dbg_ref_req;
sdram_controller u_sdram_controller(
.i_arst(i_arst), //Positive Controller Reset
.i_sysclk(i_sysclk), //Controller Clock (100MHz)
.i_sdrclk(i_sdrclk), //t_su and t_ac clock. Double sysclk (200MHz)
.i_tACclk(i_tACclk), //t_ac clock. Also double sysclk, but different pll for tuning
.i_pll_locked(1'b1), //There exists a pll locked output from the pll, not sure why they don't use it.
.i_we(w_write), //Write enable. Can only be de-asserted if i_last is asserted and o_wr_ack is sampled high.
.i_re(w_read), //Read enable. Can only be de-asserted if i_last is asserted and o_rd_ack is sampled high.
.i_last(w_last), //Set to high to indicate the last transfer of a burst write or read.
.i_addr(addr_mux_out), //SDRAM physical address B R C. For half rate, only even addresses.
.i_din(r_write_data), //Data to write to SDRAM. Twice normal width when running at half speed (hence the even addresses)
.i_dm(r_dm), //dm (r_dm)
.o_dout(w_data_o), //Data read from SDRAM, doubled as above.
.o_sdr_init_done(o_sdr_init_done), //Indicates that the SDRAM initialization is done.
.o_wr_ack(w_wr_ack), //Write acknowledge, handshake with we
.o_rd_ack(w_rd_ack), //Read acknowledge, handshake with re
.o_rd_valid(w_rd_valid),//Read valid. The data on o_dout is valid
.o_sdr_CKE(w_sdr_CKE),
.o_sdr_n_CS(w_sdr_n_CS),
.o_sdr_n_RAS(w_sdr_n_RAS),
.o_sdr_n_CAS(w_sdr_n_CAS),
.o_sdr_n_WE(w_sdr_n_WE),
.o_sdr_BA(w_sdr_BA),
.o_sdr_ADDR(w_sdr_ADDR),
.o_sdr_DATA(w_sdr_DATA),
.o_sdr_DATA_oe(w_sdr_DATA_oe),
.i_sdr_DATA({{16'b0}, {i_sdr_DATA}}),
.o_sdr_DQM(w_sdr_DQM),
//Does include debug signals.
.o_sdr_state(o_sdr_state),
.o_dbg_tRTW_done ( o_dbg_tRTW_done ),
.o_dbg_ref_req ( o_dbg_ref_req ),
.o_dbg_wr_ack ( o_dbg_wr_ack ),
.o_dbg_rd_ack ( o_dbg_rd_ack ),
.o_dbg_n_CS ( o_dbg_n_CS ),
.o_dbg_n_RAS ( o_dbg_n_RAS ),
.o_dbg_n_CAS ( o_dbg_n_CAS ),
.o_dbg_n_WE ( o_dbg_n_WE ),
.o_dbg_BA ( o_dbg_BA ),
.o_dbg_ADDR ( o_dbg_ADDR ),
.o_dbg_DATA_out ( o_dbg_DATA_out ),
.o_dbg_DATA_in ( o_dbg_DATA_in )
);
endmodule

View File

@@ -0,0 +1,97 @@
module spi_controller(
input i_clk,
input i_rst,
input i_cs,
input i_rwb,
input [1:0] i_addr,
input [7:0] i_data,
output logic [7:0] o_data,
output o_spi_cs,
output o_spi_clk,
output o_spi_mosi,
input i_spi_miso
);
// We need a speed register
// an input data register
// and an output data register
// and then a control register for cs
logic [7:0] r_baud_rate;
logic [7:0] r_input_data;
logic [7:0] r_output_data;
logic [7:0] r_control;
logic [8:0] r_clock_counter;
logic active;
logic [2:0] count;
logic spi_clk;
logic r_spi_mosi;
assign o_spi_cs = ~r_control[0];
assign o_spi_clk = spi_clk;
assign o_spi_mosi = r_spi_mosi;
always @(negedge i_clk) begin
if (i_rst) begin
r_baud_rate <= 8'h1;
r_input_data <= '0;
r_output_data <= '0;
r_control <= '0;
r_clock_counter <= '0;
count <= '0;
spi_clk <= '0;
active <= '0;
end else begin
if (~i_rwb & i_cs) begin
unique case (i_addr)
0: r_baud_rate <= i_data;
1:;
2: begin
r_output_data <= i_data;
active <= '1;
end
3: r_control <= i_data;
endcase
end
if (active) begin
r_spi_mosi <= r_output_data[7];
r_clock_counter <= r_clock_counter + 9'b1;
if (r_clock_counter >= r_baud_rate) begin
r_clock_counter <= '0;
spi_clk <= ~spi_clk;
// rising edge
if (spi_clk == '0) begin
r_output_data <= r_output_data << 1;
count <= count + 1;
end
// falling edge
if (spi_clk == '1) begin
r_input_data <= {r_input_data[6:0], i_spi_miso};
if (count == '0) begin
active <= '0;
end
end
end
end
end
end
always_comb begin
unique case (i_addr)
0: o_data = r_baud_rate;
1: o_data = r_input_data;
2:;
3: o_data = {active, r_control[6:0]};
endcase
end
endmodule

View File

@@ -0,0 +1,256 @@
module super6502
(
input logic i_sysclk, // Controller Clock (100MHz)
input logic i_sdrclk, // t_su and t_wd clock (200MHz)
input logic i_tACclk, // t_ac clock (200MHz)
input [7:0] cpu_data_in,
input cpu_sync,
input cpu_rwb,
input pll_in,
input button_reset,
input pll_cpu_locked,
input clk_50,
input clk_2,
input logic [15:0] cpu_addr,
output logic [7:0] cpu_data_out,
output logic [7:0] cpu_data_oe,
output logic cpu_irqb,
output logic cpu_nmib,
output logic cpu_rdy,
output logic cpu_resb,
output logic pll_cpu_reset,
output logic cpu_phi2,
output logic [7:0] leds,
output logic o_pll_reset,
output logic o_sdr_CKE,
output logic o_sdr_n_CS,
output logic o_sdr_n_WE,
output logic o_sdr_n_RAS,
output logic o_sdr_n_CAS,
output logic [1:0] o_sdr_BA,
output logic [12:0] o_sdr_ADDR,
input logic [15:0] i_sdr_DATA,
output logic [15:0] o_sdr_DATA,
output logic [15:0] o_sdr_DATA_oe,
output logic [1:0] o_sdr_DQM,
input uart_rx,
output uart_tx,
output sd_cs,
output spi_clk,
output spi_mosi,
input spi_miso
);
assign pll_cpu_reset = '1;
assign o_pll_reset = '1;
assign cpu_data_oe = {8{cpu_rwb}};
assign cpu_nmib = '1;
logic w_wait;
assign cpu_rdy = ~w_wait;
assign cpu_phi2 = clk_2;
logic w_sdr_init_done;
always @(posedge clk_2) begin
if (button_reset == '0) begin
cpu_resb <= '0;
end
else begin
if (cpu_resb == '0 && w_sdr_init_done) begin
cpu_resb <= '1;
end
end
end
logic w_rom_cs;
logic w_leds_cs;
logic w_sdram_cs;
logic w_timer_cs;
logic w_multiplier_cs;
logic w_divider_cs;
logic w_uart_cs;
logic w_spi_cs;
addr_decode u_addr_decode(
.i_addr(cpu_addr),
.o_rom_cs(w_rom_cs),
.o_leds_cs(w_leds_cs),
.o_timer_cs(w_timer_cs),
.o_multiplier_cs(w_multiplier_cs),
.o_divider_cs(w_divider_cs),
.o_uart_cs(w_uart_cs),
.o_spi_cs(w_spi_cs),
.o_sdram_cs(w_sdram_cs)
);
logic [7:0] w_rom_data_out;
logic [7:0] w_leds_data_out;
logic [7:0] w_timer_data_out;
logic [7:0] w_multiplier_data_out;
logic [7:0] w_divider_data_out;
logic [7:0] w_uart_data_out;
logic [7:0] w_spi_data_out;
logic [7:0] w_sdram_data_out;
always_comb begin
if (w_rom_cs)
cpu_data_out = w_rom_data_out;
else if (w_leds_cs)
cpu_data_out = w_leds_data_out;
else if (w_timer_cs)
cpu_data_out = w_timer_data_out;
else if (w_multiplier_cs)
cpu_data_out = w_multiplier_data_out;
else if (w_divider_cs)
cpu_data_out = w_divider_data_out;
else if (w_uart_cs)
cpu_data_out = w_uart_data_out;
else if (w_spi_cs)
cpu_data_out = w_spi_data_out;
else if (w_sdram_cs)
cpu_data_out = w_sdram_data_out;
else
cpu_data_out = 'x;
end
rom #(.DATA_WIDTH(8), .ADDR_WIDTH(12)) u_rom(
.addr(cpu_addr[11:0]),
.clk(clk_2),
.data(w_rom_data_out)
);
leds u_leds(
.clk(clk_2),
.i_data(cpu_data_in),
.o_data(w_leds_data_out),
.cs(w_leds_cs),
.rwb(cpu_rwb),
.o_leds(leds)
);
logic w_timer_irqb;
timer u_timer(
.clk(clk_2),
.reset(~cpu_resb),
.i_data(cpu_data_in),
.o_data(w_timer_data_out),
.cs(w_timer_cs),
.rwb(cpu_rwb),
.addr(cpu_addr[1:0]),
.irqb(w_timer_irqb)
);
multiplier u_multiplier(
.clk(clk_2),
.reset(~cpu_resb),
.i_data(cpu_data_in),
.o_data(w_multiplier_data_out),
.cs(w_multiplier_cs),
.rwb(cpu_rwb),
.addr(cpu_addr[2:0])
);
divider_wrapper u_divider(
.clk(clk_2),
.divclk(clk_50),
.reset(~cpu_resb),
.i_data(cpu_data_in),
.o_data(w_divider_data_out),
.cs(w_divider_cs),
.rwb(cpu_rwb),
.addr(cpu_addr[2:0])
);
logic w_uart_irqb;
uart_wrapper u_uart(
.clk(clk_2),
.clk_50(clk_50),
.reset(~cpu_resb),
.i_data(cpu_data_in),
.o_data(w_uart_data_out),
.cs(w_uart_cs),
.rwb(cpu_rwb),
.addr(cpu_addr[0]),
.rx_i(uart_rx),
.tx_o(uart_tx),
.irqb(w_uart_irqb)
);
spi_controller spi_controller(
.i_clk(clk_2),
.i_rst(~cpu_resb),
.i_cs(w_spi_cs),
.i_rwb(cpu_rwb),
.i_addr(cpu_addr[1:0]),
.i_data(cpu_data_in),
.o_data(w_spi_data_out),
.o_spi_cs(sd_cs),
.o_spi_clk(spi_clk),
.o_spi_mosi(spi_mosi),
.i_spi_miso(spi_miso)
);
sdram_adapter u_sdram_adapter(
.i_cpuclk(clk_2),
.i_arst(~button_reset),
.i_sysclk(i_sysclk),
.i_sdrclk(i_sdrclk),
.i_tACclk(i_tACclk),
.i_cs(w_sdram_cs),
.i_rwb(cpu_rwb),
.i_addr(cpu_addr),
.i_data(cpu_data_in),
.o_data(w_sdram_data_out),
.o_sdr_init_done(w_sdr_init_done),
.o_wait(w_wait),
.o_sdr_CKE(o_sdr_CKE),
.o_sdr_n_CS(o_sdr_n_CS),
.o_sdr_n_RAS(o_sdr_n_RAS),
.o_sdr_n_CAS(o_sdr_n_CAS),
.o_sdr_n_WE(o_sdr_n_WE),
.o_sdr_BA(o_sdr_BA),
.o_sdr_ADDR(o_sdr_ADDR),
.o_sdr_DATA(o_sdr_DATA),
.o_sdr_DATA_oe(o_sdr_DATA_oe),
.i_sdr_DATA(i_sdr_DATA),
.o_sdr_DQM(o_sdr_DQM)
);
interrupt_controller u_interrupt_controller(
.clk(clk_2),
.reset(~cpu_resb),
.i_data(cpu_data_in),
.o_data(w_irq_data_out),
.cs(w_irq_cs),
.rwb(cpu_rwb),
.irqb_master(cpu_irqb),
.irqb0(w_timer_irqb),
.irqb1('1),
.irqb2('1),
.irqb3('1),
.irqb4('1),
.irqb5('1),
.irqb6('1),
.irqb7('1)
);
endmodule

131
hw/efinix_fpga/src/timer.sv Normal file
View File

@@ -0,0 +1,131 @@
module timer
(
input clk,
input reset,
input [7:0] i_data,
output logic [7:0] o_data,
input cs,
input rwb,
input [1:0] addr,
output logic irqb
);
//new idea for timer:
//it can either be oneshot or repeating
//it can either cause an interrupt or not.
//if you want it to do both, add another timer.
/*
Addr Read Write
0 Counter Low Latch Low
1 Counter High Latch High
2 Divisor Divisor
3 Status Control
*/
logic [15:0] timer_latch, timer_counter;
//control register
// bit 0: Enable interrupts
// bit 1: Enable 1 shot mode
//writing to latch low starts the timer
logic [7:0] divisor, status, control;
logic count_en;
assign status[0] = count_en;
logic [15:0] pulsecount;
//I think this should be negedge so that writes go through
always @(negedge clk) begin
if (reset) begin
count_en = '0;
timer_counter <= '0;
pulsecount <= '0;
timer_latch <= '1;
divisor <= '0;
control <= '0;
irqb <= '1;
end else begin
if (count_en) begin
if (pulsecount[15:8] == divisor) begin
timer_counter <= timer_counter + 16'b1;
pulsecount <= '0;
end else begin
pulsecount <= pulsecount + 16'b1;
end
end
if (timer_counter == timer_latch) begin
// if interrupts are enabled
if (control[0]) begin
irqb <= '0;
end
// if oneshot mode is enabled
if (control[1]) begin
count_en <= '0;
end else begin
timer_counter <= '0;
end
end
if (cs & rwb) begin
irqb <= '1;
end
if (cs & ~rwb) begin
case (addr)
2'h0: begin
count_en <= '1;
timer_latch[7:0] <= i_data;
end
2'h1: begin
timer_latch[15:8] <= i_data;
end
2'h2: begin
divisor <= i_data;
end
2'h3: begin
control <= i_data;
end
endcase
end
end
end
always_comb begin
o_data = '0;
unique case (addr)
2'h0: begin
o_data = timer_counter[7:0];
end
2'h1: begin
o_data = timer_counter[15:8];
end
2'h2: begin
o_data = divisor;
end
2'h3: begin
o_data = status;
end
endcase
end
endmodule

View File

@@ -0,0 +1,118 @@
module uart_wrapper(
input clk,
input clk_50,
input reset,
input [7:0] i_data,
output logic [7:0] o_data,
input cs,
input rwb,
input addr,
input rx_i,
output tx_o,
output logic irqb
);
logic [7:0] status, control;
logic tx_busy, rx_busy;
logic rx_data_valid, rx_error, rx_parity_error;
logic baud_x16_ce;
logic tx_en;
logic [7:0] tx_data, rx_data;
uart u_uart(
.tx_o ( tx_o ),
.rx_i ( rx_i ),
.tx_busy ( tx_busy ),
.rx_data ( rx_data ),
.rx_data_valid ( rx_data_valid ),
.rx_error ( rx_error ),
.rx_parity_error ( rx_parity_error ),
.rx_busy ( rx_busy ),
.baud_x16_ce ( baud_x16_ce ),
.clk ( clk_50 ),
.reset ( reset ),
.tx_data ( tx_data ),
.baud_rate ( baud_rate ),
.tx_en ( tx_en )
);
enum bit [1:0] {READY, WAIT, TRANSMIT} state, next_state;
always_ff @(posedge clk_50) begin
if (reset) begin
state = READY;
irqb <= '1;
end else begin
state <= next_state;
end
end
always_ff @(negedge clk) begin
status[1] <= tx_busy | tx_en;
status[0] <= status[0] | rx_data_valid;
if (cs & ~addr & rwb) begin
status[0] <= 0;
end
if (cs & ~rwb) begin
case (addr)
1'b0: begin
tx_data <= i_data;
end
1'b1: begin
control <= i_data;
end
endcase
end
end
always_comb begin
case (addr)
1'b0: begin
o_data = rx_data;
end
1'b1: begin
o_data = status;
end
endcase
end
always_comb begin
next_state = state;
tx_en = 1'b0;
case (state)
READY: begin
if (cs & ~rwb && addr == 1'b0) begin //write to transmit
tx_en = 1'b1;
next_state = WAIT;
end
end
WAIT: begin
tx_en = 1'b1;
if (tx_busy) begin
next_state = TRANSMIT;
end
end
TRANSMIT: begin
if (~tx_busy) begin
next_state = READY;
end
end
endcase
end
endmodule