import Vivado project
This commit is contained in:
parent
811a2c17d9
commit
e3ff5106cf
27 changed files with 7285 additions and 1 deletions
46
rtl/src/bram_tdp.v
Normal file
46
rtl/src/bram_tdp.v
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
`timescale 1ns / 1ps
|
||||
// taken from https://danstrother.com/2010/09/11/inferring-rams-in-fpgas/
|
||||
// modified for one read/write-port and one read-only-port,
|
||||
/// A parameterized, inferable, true dual-port, dual-clock block RAM in Verilog.
|
||||
|
||||
module bram_tdp #(
|
||||
parameter DATA = 72,
|
||||
parameter ADDR = 10
|
||||
) (
|
||||
// Port A
|
||||
input wire a_clk,
|
||||
input wire a_rd,
|
||||
input wire a_wr,
|
||||
input wire [ADDR-1:0] a_addr,
|
||||
input wire [DATA-1:0] a_din,
|
||||
output reg [DATA-1:0] a_dout,
|
||||
// Port B
|
||||
input wire b_clk,
|
||||
input wire [ADDR-1:0] b_addr,
|
||||
output reg [DATA-1:0] b_dout,
|
||||
input wire b_rd
|
||||
);
|
||||
|
||||
// Shared memory
|
||||
reg [DATA-1:0] mem [(2**ADDR)-1:0];
|
||||
|
||||
wire a_en = a_rd || a_wr;
|
||||
|
||||
// Port A
|
||||
always @(posedge a_clk) begin
|
||||
if(a_en)
|
||||
begin
|
||||
if(a_wr)
|
||||
mem[a_addr] <= a_din;
|
||||
else if(a_rd)
|
||||
a_dout <= mem[a_addr];
|
||||
end
|
||||
end
|
||||
|
||||
// Port B
|
||||
always @(posedge b_clk) begin
|
||||
if(b_rd)
|
||||
b_dout <= mem[b_addr];
|
||||
end
|
||||
|
||||
endmodule
|
||||
83
rtl/src/cpuclk.v
Normal file
83
rtl/src/cpuclk.v
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module cpu_clkgen(
|
||||
input wire rst,
|
||||
input wire clk100,
|
||||
output wire cpuclk,
|
||||
output wire dram_refclk,
|
||||
output wire pixclk,
|
||||
output wire locked
|
||||
);
|
||||
|
||||
wire cpuclk_pre, clk_fb, refclk_pre, pixclk_pre;
|
||||
|
||||
MMCME2_BASE #(
|
||||
.BANDWIDTH("OPTIMIZED"), // Jitter programming (OPTIMIZED, HIGH, LOW)
|
||||
.CLKFBOUT_MULT_F(10.0), // Multiply value for all CLKOUT (2.000-64.000).
|
||||
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB (-360.000-360.000).
|
||||
.CLKIN1_PERIOD(10.0), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
|
||||
// CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128)
|
||||
.CLKOUT0_DIVIDE_F(12.0), // Divide amount for CLKOUT0 (1.000-128.000).
|
||||
.CLKOUT1_DIVIDE(5),
|
||||
.CLKOUT2_DIVIDE(40), // 40 = 25MHz pixel clock (should be 25.175MHz per spec) for 640x480
|
||||
//.CLKOUT2_DIVIDE(25), // 25 = 40MHz pixel clock for 800x600
|
||||
//.CLKOUT2_DIVIDE(15), // 15 = 66.66MHz pixel clock (should be 65.0Mhz per spec) for 1024x768
|
||||
.CLKOUT3_DIVIDE(1),
|
||||
.CLKOUT4_DIVIDE(1),
|
||||
.CLKOUT5_DIVIDE(1),
|
||||
.CLKOUT6_DIVIDE(1),
|
||||
// CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (0.01-0.99).
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT2_DUTY_CYCLE(0.5),
|
||||
.CLKOUT3_DUTY_CYCLE(0.5),
|
||||
.CLKOUT4_DUTY_CYCLE(0.5),
|
||||
.CLKOUT5_DUTY_CYCLE(0.5),
|
||||
.CLKOUT6_DUTY_CYCLE(0.5),
|
||||
// CLKOUT0_PHASE - CLKOUT6_PHASE: Phase offset for each CLKOUT (-360.000-360.000).
|
||||
.CLKOUT0_PHASE(0.0),
|
||||
.CLKOUT1_PHASE(0.0),
|
||||
.CLKOUT2_PHASE(0.0),
|
||||
.CLKOUT3_PHASE(0.0),
|
||||
.CLKOUT4_PHASE(0.0),
|
||||
.CLKOUT5_PHASE(0.0),
|
||||
.CLKOUT6_PHASE(0.0),
|
||||
.CLKOUT4_CASCADE("FALSE"), // Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE)
|
||||
.DIVCLK_DIVIDE(1), // Master division value (1-106)
|
||||
.REF_JITTER1(0.010), // Reference input jitter in UI (0.000-0.999).
|
||||
.STARTUP_WAIT("FALSE") // Delays DONE until MMCM is locked (FALSE, TRUE)
|
||||
)
|
||||
|
||||
MMCME2_BASE_inst (
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
// Clock Outputs: 1-bit (each) output: User configurable clock outputs
|
||||
.CLKOUT0(cpuclk_pre), // 1-bit output: CLKOUT0
|
||||
.CLKOUT0B(), // 1-bit output: Inverted CLKOUT0
|
||||
.CLKOUT1(refclk_pre), // 1-bit output: CLKOUT1
|
||||
.CLKOUT1B(), // 1-bit output: Inverted CLKOUT1
|
||||
.CLKOUT2(pixclk_pre), // 1-bit output: CLKOUT2
|
||||
.CLKOUT2B(), // 1-bit output: Inverted CLKOUT2
|
||||
.CLKOUT3(), // 1-bit output: CLKOUT3
|
||||
.CLKOUT3B(), // 1-bit output: Inverted CLKOUT3
|
||||
.CLKOUT4(), // 1-bit output: CLKOUT4
|
||||
.CLKOUT5(), // 1-bit output: CLKOUT5
|
||||
.CLKOUT6(), // 1-bit output: CLKOUT6
|
||||
// Feedback Clocks: 1-bit (each) output: Clock feedback ports
|
||||
.CLKFBOUT(clk_fb), // 1-bit output: Feedback clock
|
||||
.CLKFBOUTB(), // 1-bit output: Inverted CLKFBOUT
|
||||
// Status Ports: 1-bit (each) output: MMCM status ports
|
||||
.LOCKED(locked), // 1-bit output: LOCK
|
||||
// Clock Inputs: 1-bit (each) input: Clock input
|
||||
.CLKIN1(clk100), // 1-bit input: Clock
|
||||
// Control Ports: 1-bit (each) input: MMCM control ports
|
||||
.PWRDWN(), // 1-bit input: Power-down
|
||||
/* verilator lint_on PINCONNECTEMPTY */
|
||||
.RST(rst), // 1-bit input: Reset
|
||||
// Feedback Clocks: 1-bit (each) input: Clock feedback ports
|
||||
.CLKFBIN(clk_fb) // 1-bit input: Feedback clock
|
||||
);
|
||||
|
||||
BUFG bufg_cpuclk(.I(cpuclk_pre), .O(cpuclk));
|
||||
BUFG bufg_refclk(.I(refclk_pre), .O(dram_refclk));
|
||||
BUFG bufg_pixclk(.I(pixclk_pre), .O(pixclk));
|
||||
endmodule
|
||||
96
rtl/src/display_clock.v
Normal file
96
rtl/src/display_clock.v
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
// Project F: Display Clocks
|
||||
// (C)2019 Will Green, Open source hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
||||
// Defaults to 25.2 and 126 MHz for 640x480 at 60 Hz
|
||||
|
||||
module display_clock #(
|
||||
MULT_MASTER=31.5, // master clock multiplier (2.000-64.000)
|
||||
DIV_MASTER=5, // master clock divider (1-106)
|
||||
DIV_5X=5.0, // 5x clock divider (1-128)
|
||||
DIV_1X=25, // 1x clock divider (1-128)
|
||||
IN_PERIOD=10.0 // period of i_clk in ns (100 MHz = 10.0 ns)
|
||||
)
|
||||
(
|
||||
input wire i_clk, // input clock
|
||||
input wire i_rst, // reset (active high)
|
||||
output wire o_clk_1x, // pixel clock
|
||||
output wire o_clk_5x, // 5x clock for 10:1 DDR SerDes
|
||||
output wire o_locked // clock locked? (active high)
|
||||
);
|
||||
|
||||
wire clk_fb; // internal clock feedback
|
||||
wire clk_1x_pre;
|
||||
wire clk_5x_pre;
|
||||
|
||||
MMCME2_BASE #(
|
||||
.BANDWIDTH("OPTIMIZED"), // Jitter programming (OPTIMIZED, HIGH, LOW)
|
||||
.CLKFBOUT_MULT_F(MULT_MASTER), // Multiply value for all CLKOUT (2.000-64.000).
|
||||
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB (-360.000-360.000).
|
||||
.CLKIN1_PERIOD(IN_PERIOD), // Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz).
|
||||
// CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128)
|
||||
.CLKOUT0_DIVIDE_F(DIV_5X), // Divide amount for CLKOUT0 (1.000-128.000).
|
||||
.CLKOUT1_DIVIDE(DIV_1X),
|
||||
.CLKOUT2_DIVIDE(1),
|
||||
.CLKOUT3_DIVIDE(1),
|
||||
.CLKOUT4_DIVIDE(1),
|
||||
.CLKOUT5_DIVIDE(1),
|
||||
.CLKOUT6_DIVIDE(1),
|
||||
// CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (0.01-0.99).
|
||||
.CLKOUT0_DUTY_CYCLE(0.5),
|
||||
.CLKOUT1_DUTY_CYCLE(0.5),
|
||||
.CLKOUT2_DUTY_CYCLE(0.5),
|
||||
.CLKOUT3_DUTY_CYCLE(0.5),
|
||||
.CLKOUT4_DUTY_CYCLE(0.5),
|
||||
.CLKOUT5_DUTY_CYCLE(0.5),
|
||||
.CLKOUT6_DUTY_CYCLE(0.5),
|
||||
// CLKOUT0_PHASE - CLKOUT6_PHASE: Phase offset for each CLKOUT (-360.000-360.000).
|
||||
.CLKOUT0_PHASE(0.0),
|
||||
.CLKOUT1_PHASE(0.0),
|
||||
.CLKOUT2_PHASE(0.0),
|
||||
.CLKOUT3_PHASE(0.0),
|
||||
.CLKOUT4_PHASE(0.0),
|
||||
.CLKOUT5_PHASE(0.0),
|
||||
.CLKOUT6_PHASE(0.0),
|
||||
.CLKOUT4_CASCADE("FALSE"), // Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE)
|
||||
.DIVCLK_DIVIDE(DIV_MASTER), // Master division value (1-106)
|
||||
.REF_JITTER1(0.010), // Reference input jitter in UI (0.000-0.999).
|
||||
.STARTUP_WAIT("FALSE") // Delays DONE until MMCM is locked (FALSE, TRUE)
|
||||
)
|
||||
MMCME2_BASE_inst (
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
// Clock Outputs: 1-bit (each) output: User configurable clock outputs
|
||||
.CLKOUT0(clk_5x_pre), // 1-bit output: CLKOUT0
|
||||
.CLKOUT0B(), // 1-bit output: Inverted CLKOUT0
|
||||
.CLKOUT1(clk_1x_pre), // 1-bit output: CLKOUT1
|
||||
.CLKOUT1B(), // 1-bit output: Inverted CLKOUT1
|
||||
.CLKOUT2(), // 1-bit output: CLKOUT2
|
||||
.CLKOUT2B(), // 1-bit output: Inverted CLKOUT2
|
||||
.CLKOUT3(), // 1-bit output: CLKOUT3
|
||||
.CLKOUT3B(), // 1-bit output: Inverted CLKOUT3
|
||||
.CLKOUT4(), // 1-bit output: CLKOUT4
|
||||
.CLKOUT5(), // 1-bit output: CLKOUT5
|
||||
.CLKOUT6(), // 1-bit output: CLKOUT6
|
||||
// Feedback Clocks: 1-bit (each) output: Clock feedback ports
|
||||
.CLKFBOUT(clk_fb), // 1-bit output: Feedback clock
|
||||
.CLKFBOUTB(), // 1-bit output: Inverted CLKFBOUT
|
||||
// Status Ports: 1-bit (each) output: MMCM status ports
|
||||
.LOCKED(o_locked), // 1-bit output: LOCK
|
||||
// Clock Inputs: 1-bit (each) input: Clock input
|
||||
.CLKIN1(i_clk), // 1-bit input: Clock
|
||||
// Control Ports: 1-bit (each) input: MMCM control ports
|
||||
.PWRDWN(), // 1-bit input: Power-down
|
||||
/* verilator lint_on PINCONNECTEMPTY */
|
||||
.RST(i_rst), // 1-bit input: Reset
|
||||
// Feedback Clocks: 1-bit (each) input: Clock feedback ports
|
||||
.CLKFBIN(clk_fb) // 1-bit input: Feedback clock
|
||||
);
|
||||
|
||||
// explicitly buffer output clocks
|
||||
BUFG bufg_clk_pix(.I(clk_1x_pre), .O(o_clk_1x));
|
||||
BUFG bufg_clk_pix_5x(.I(clk_5x_pre), .O(o_clk_5x));
|
||||
|
||||
endmodule
|
||||
165
rtl/src/dram_bridge.v
Normal file
165
rtl/src/dram_bridge.v
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module dram_bridge #(ADDR_WIDTH = 32, WIDTH = 32)
|
||||
(
|
||||
// local bus
|
||||
input wire [ADDR_WIDTH-1:0] mem_addr,
|
||||
output wire [WIDTH-1:0] mem_read_data,
|
||||
input wire [WIDTH-1:0] mem_write_data,
|
||||
input wire mem_read_enable,
|
||||
input wire mem_write_enable,
|
||||
output wire mem_wait,
|
||||
|
||||
input wire rst_n,
|
||||
input wire dram_front_clk,
|
||||
input wire dram_refclk,
|
||||
|
||||
// DDR3 SDRAM
|
||||
inout wire [15:0] ddr3_dq,
|
||||
inout wire [1:0] ddr3_dqs_n,
|
||||
inout wire [1:0] ddr3_dqs_p,
|
||||
|
||||
output wire [13:0] ddr3_addr,
|
||||
output wire [2:0] ddr3_ba,
|
||||
output wire ddr3_ras_n,
|
||||
output wire ddr3_cas_n,
|
||||
output wire ddr3_we_n,
|
||||
output wire ddr3_reset_n,
|
||||
output wire [0:0] ddr3_ck_p,
|
||||
output wire [0:0] ddr3_ck_n,
|
||||
output wire [0:0] ddr3_cke,
|
||||
output wire [0:0] ddr3_cs_n,
|
||||
output wire [1:0] ddr3_dm,
|
||||
output wire [0:0] ddr3_odt
|
||||
);
|
||||
|
||||
localparam DRAM_ADDR_WIDTH = 28, DRAM_DATA_WIDTH = 128, DRAM_MASK_WIDTH = 16;
|
||||
wire [DRAM_ADDR_WIDTH-1:0] app_addr;
|
||||
wire [2:0] app_cmd;
|
||||
wire app_en;
|
||||
wire app_rdy;
|
||||
wire [DRAM_DATA_WIDTH-1:0] app_rd_data;
|
||||
wire app_rd_data_end;
|
||||
wire app_rd_data_valid;
|
||||
wire [DRAM_DATA_WIDTH-1:0] app_wdf_data;
|
||||
wire app_wdf_end;
|
||||
wire [DRAM_MASK_WIDTH-1:0] app_wdf_mask;
|
||||
wire app_wdf_rdy;
|
||||
wire app_sr_active;
|
||||
wire app_ref_ack;
|
||||
wire app_zq_ack;
|
||||
wire app_wdf_wren;
|
||||
wire [11:0] device_temp;
|
||||
wire ui_clk, ui_rst_sync;
|
||||
wire init_calib_complete;
|
||||
|
||||
localparam CMD_READ = 3'b1;
|
||||
localparam CMD_WRITE = 3'b0;
|
||||
|
||||
mig_dram_0 dram0(
|
||||
// Inouts
|
||||
.ddr3_dq(ddr3_dq),
|
||||
.ddr3_dqs_n(ddr3_dqs_n),
|
||||
.ddr3_dqs_p(ddr3_dqs_p),
|
||||
// Outputs
|
||||
.ddr3_addr(ddr3_addr),
|
||||
.ddr3_ba(ddr3_ba),
|
||||
.ddr3_ras_n(ddr3_ras_n),
|
||||
.ddr3_cas_n(ddr3_cas_n),
|
||||
.ddr3_we_n(ddr3_we_n),
|
||||
.ddr3_reset_n(ddr3_reset_n),
|
||||
.ddr3_ck_p(ddr3_ck_p),
|
||||
.ddr3_ck_n(ddr3_ck_n),
|
||||
.ddr3_cke(ddr3_cke),
|
||||
.ddr3_cs_n(ddr3_cs_n),
|
||||
.ddr3_dm(ddr3_dm),
|
||||
.ddr3_odt(ddr3_odt),
|
||||
// Application interface ports
|
||||
.app_addr (app_addr),
|
||||
.app_cmd (app_cmd),
|
||||
.app_en (app_en),
|
||||
.app_wdf_data (app_wdf_data),
|
||||
.app_wdf_mask (app_wdf_mask),
|
||||
.app_wdf_end (app_wdf_end),
|
||||
.app_wdf_wren (app_wdf_wren),
|
||||
.app_rd_data (app_rd_data),
|
||||
.app_rd_data_end (app_rd_data_end),
|
||||
.app_rd_data_valid (app_rd_data_valid),
|
||||
.app_rdy (app_rdy),
|
||||
.app_wdf_rdy (app_wdf_rdy),
|
||||
.app_sr_req (1'b0),
|
||||
.app_ref_req (1'b0),
|
||||
.app_zq_req (1'b0),
|
||||
.app_sr_active (app_sr_active),
|
||||
.app_ref_ack (app_ref_ack),
|
||||
.app_zq_ack (app_zq_ack),
|
||||
.ui_clk (ui_clk),
|
||||
.ui_clk_sync_rst (ui_rst_sync),
|
||||
|
||||
// System Clock Ports
|
||||
.sys_clk_i (dram_front_clk),
|
||||
// Reference Clock Ports
|
||||
.clk_ref_i (dram_refclk),
|
||||
.device_temp (device_temp),
|
||||
.init_calib_complete (init_calib_complete),
|
||||
.sys_rst (rst_n)
|
||||
);
|
||||
|
||||
// reg [DRAM_DATA_WIDTH-1:0] read_cache;
|
||||
// reg [ADDR_WIDTH-1:0] cached_addr;
|
||||
// wire cache_hit = cached_addr == mem_addr;
|
||||
// wire [DRAM_DATA_WIDTH-1:0] read_data_wrapper = cache_hit ? read_cache : app_rd_data;
|
||||
|
||||
reg [WIDTH-1:0] read_buf;
|
||||
reg read_inprogress = 0;
|
||||
|
||||
assign app_rd_data_end = 1'b1;
|
||||
//assign app_wdf_mask = 16'b1111111111111100;
|
||||
|
||||
// addresses on the memory interface are aligned to 16 bytes
|
||||
// and 28 bits wide (=256MB)
|
||||
assign app_addr = { mem_addr[DRAM_ADDR_WIDTH:4], 4'b0000 };
|
||||
//assign app_addr = { 28'b0 };
|
||||
|
||||
// select a word from the 128 bits transferred by the dram controller
|
||||
// according to the lower bits of the address (ignoring bits 1:0)
|
||||
wire [WIDTH-1:0] read_word;
|
||||
wire [1:0] word_sel = mem_addr[3:2];
|
||||
|
||||
assign read_word = word_sel == 3'b11 ? app_rd_data[31:0] :
|
||||
word_sel == 3'b10 ? app_rd_data[63:32] :
|
||||
word_sel == 3'b01 ? app_rd_data[95:64] :
|
||||
app_rd_data[127:96];
|
||||
|
||||
assign mem_read_data = app_rd_data_valid ? read_word : read_buf;
|
||||
|
||||
// set the write mask according to the lower bits of the address
|
||||
// (ignoring bit 0)
|
||||
assign app_wdf_mask = word_sel == 3'b11 ? 16'b1111111111110000 :
|
||||
word_sel == 3'b10 ? 16'b1111111100001111 :
|
||||
word_sel == 3'b01 ? 16'b1111000011111111 :
|
||||
16'b0000111111111111 ;
|
||||
|
||||
wire write_ready = mem_write_enable & app_wdf_rdy & app_rdy;
|
||||
assign app_wdf_wren = mem_write_enable & write_ready;
|
||||
assign app_wdf_end = mem_write_enable & write_ready;
|
||||
assign app_wdf_data = { {4{mem_write_data}} };
|
||||
|
||||
assign mem_wait = (mem_read_enable & ~read_inprogress) |
|
||||
(mem_write_enable & (~app_wdf_rdy | ~app_rdy)) |
|
||||
(read_inprogress & ~app_rd_data_valid);
|
||||
|
||||
assign app_en = (mem_read_enable & ~read_inprogress) |
|
||||
(mem_write_enable & write_ready);
|
||||
assign app_cmd = mem_read_enable ? CMD_READ : CMD_WRITE;
|
||||
|
||||
always @(posedge dram_front_clk)
|
||||
begin
|
||||
if(mem_read_enable & ~read_inprogress & app_rdy)
|
||||
read_inprogress <= 1;
|
||||
if(read_inprogress & app_rd_data_valid)
|
||||
read_inprogress <= 0;
|
||||
if(mem_read_enable & app_rd_data_valid)
|
||||
read_buf <= mem_read_data;
|
||||
end
|
||||
endmodule
|
||||
53
rtl/src/fifo.v
Normal file
53
rtl/src/fifo.v
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
// a simple fifo
|
||||
module fifo #(parameter DATA_WIDTH = 8, ADDR_WIDTH = 4)(
|
||||
input wire clk,
|
||||
input wire reset,
|
||||
input wire wr_en,
|
||||
input wire rd_en,
|
||||
input wire [DATA_WIDTH-1:0] wr_data,
|
||||
output wire [DATA_WIDTH-1:0] rd_data,
|
||||
output wire wr_full,
|
||||
output wire rd_empty
|
||||
);
|
||||
|
||||
reg [DATA_WIDTH-1:0] mem [0:2**ADDR_WIDTH-1];
|
||||
reg [ADDR_WIDTH:0] head_x = 0; // head and tail have one extra bit
|
||||
reg [ADDR_WIDTH:0] tail_x = 0; // for detecting overflows
|
||||
wire [ADDR_WIDTH-1:0] head = head_x[ADDR_WIDTH-1:0];
|
||||
wire [ADDR_WIDTH-1:0] tail = tail_x[ADDR_WIDTH-1:0];
|
||||
|
||||
assign rd_data = mem[tail];
|
||||
// the fifo is full when head and tail pointer are the same
|
||||
// and the extra bits differ (a wraparound occured)
|
||||
assign wr_full = (head == tail) && (head_x[ADDR_WIDTH] != tail_x[ADDR_WIDTH]);
|
||||
// the fifo is empty when head and tail pointer are the same
|
||||
// and the extra bits are the same (no wraparound)
|
||||
assign rd_empty = (head == tail) && (head_x[ADDR_WIDTH] == tail_x[ADDR_WIDTH]);
|
||||
|
||||
// Writing to FIFO
|
||||
always @(posedge clk) begin
|
||||
if (reset)
|
||||
head_x <= 0;
|
||||
else if (wr_en)
|
||||
begin
|
||||
mem[head] <= wr_data;
|
||||
// move head, possible wraparound
|
||||
head_x <= head_x + 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// Reading from FIFO
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
tail_x <= 0;
|
||||
else if (rd_en)
|
||||
begin
|
||||
// rd_data always has current tail data
|
||||
// move tail, possible wraparound
|
||||
tail_x <= tail_x + 1'b1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
116
rtl/src/fifo_testbench.v
Normal file
116
rtl/src/fifo_testbench.v
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
`timescale 1ns / 1ns
|
||||
`default_nettype none
|
||||
|
||||
module fifo_testbench();
|
||||
// Test signals
|
||||
reg clk = 0;
|
||||
reg reset = 0;
|
||||
reg wr_en = 0;
|
||||
reg rd_en = 0;
|
||||
reg [7:0] wr_data = 0;
|
||||
wire [7:0] rd_data;
|
||||
wire wr_full;
|
||||
wire rd_empty;
|
||||
|
||||
parameter CLOCK_NS = 10;
|
||||
|
||||
// Unit Under Test
|
||||
fifo #(
|
||||
.DATA_WIDTH(8),
|
||||
.ADDR_WIDTH(4)
|
||||
) UUT (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.wr_en(wr_en),
|
||||
.rd_en(rd_en),
|
||||
.rd_data(rd_data),
|
||||
.wr_data(wr_data),
|
||||
.wr_full(wr_full),
|
||||
.rd_empty(rd_empty)
|
||||
);
|
||||
|
||||
// testbench clock
|
||||
always
|
||||
#(CLOCK_NS/2) clk <= ~clk;
|
||||
|
||||
initial
|
||||
begin
|
||||
// issue reset
|
||||
reset = 1'b1;
|
||||
#10
|
||||
reset = 1'b0;
|
||||
#10
|
||||
|
||||
// Write two bytes
|
||||
wr_data <= 8'hAB;
|
||||
wr_en <= 1'b1;
|
||||
#10;
|
||||
wr_data <= 8'hCD;
|
||||
wr_en <= 1'b1;
|
||||
#10;
|
||||
wr_en <= 1'b0;
|
||||
#10
|
||||
|
||||
// read fifo tail
|
||||
if (rd_data == 8'hAB)
|
||||
$display("Pass - Byte 1");
|
||||
else
|
||||
$display("Failed - Byte 2");
|
||||
|
||||
// read/remove byte from tail
|
||||
rd_en <= 1'b1;
|
||||
#10
|
||||
// check next byte
|
||||
if (rd_data == 8'hCD)
|
||||
$display("Pass - Byte 2");
|
||||
else
|
||||
$display("Failed - Byte 2");
|
||||
|
||||
// remove 2nd byte
|
||||
rd_en <= 1'b1;
|
||||
#10
|
||||
|
||||
rd_en <= 1'b0;
|
||||
#10
|
||||
|
||||
// Write until full
|
||||
rd_en <= 1'b0;
|
||||
wr_en <= 1'b0;
|
||||
|
||||
for (integer i = 0; i < 16; i = i + 1) begin
|
||||
wr_data <= i;
|
||||
wr_en <= 1'b1;
|
||||
#10;
|
||||
end
|
||||
wr_en <= 1'b0;
|
||||
|
||||
if (wr_full)
|
||||
$display("Pass - Fifo full");
|
||||
else
|
||||
$display("Failed - Fifo full");
|
||||
|
||||
|
||||
// read until empty
|
||||
rd_en <= 1'b0;
|
||||
wr_en <= 1'b0;
|
||||
|
||||
for (integer i = 0; i < 16; i = i + 1) begin
|
||||
rd_en <= 1'b1;
|
||||
#10;
|
||||
end
|
||||
rd_en <= 1'b0;
|
||||
|
||||
if (rd_empty)
|
||||
$display("Pass - Fifo empty");
|
||||
else
|
||||
$display("Failed - Fifo empty");
|
||||
$finish();
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
// Required to dump signals
|
||||
$dumpfile("fifo_tb_dump.vcd");
|
||||
$dumpvars(0);
|
||||
end
|
||||
endmodule
|
||||
73
rtl/src/irqctrl.v
Normal file
73
rtl/src/irqctrl.v
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
module irqctrl #(IRQ_LINES = 2, IRQ_DELAY_WIDTH = 4) (
|
||||
input wire clk,
|
||||
input wire [IRQ_LINES-1:0] irq_in,
|
||||
input wire cs,
|
||||
input wire wr_en,
|
||||
input wire irq_wr_seten,
|
||||
output wire [IRQ_LINES-1:0] rd_data,
|
||||
output wire irq_out
|
||||
);
|
||||
|
||||
reg [IRQ_LINES-1:0] irq_status; // a 1 bit here means we have seen an interrupt on that line
|
||||
reg [IRQ_LINES-1:0] irq_mask; // a bit in irq_status is only set if the corresponding bit in irq_mask is not set
|
||||
// irq_mask is set from irq_status when an interrupt occurs (ie irq_out is set)
|
||||
|
||||
reg irq_enabled; // globally enable/disable irq_out
|
||||
reg [IRQ_DELAY_WIDTH-1:0] irq_delay; // counter to delay irq_out for a few cycles
|
||||
reg irq_delaying; // delay is active
|
||||
|
||||
wire irq_pending = (irq_status != 0);
|
||||
|
||||
assign rd_data = irq_mask;
|
||||
assign irq_out = irq_enabled && irq_pending && !irq_delaying;
|
||||
|
||||
// irq_status and irq_pending flags
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(irq_out) // when an interrupt is being signaled to the cpu,
|
||||
irq_status <= 0; // clear irq status, status will be copied to irq_mask (see below)
|
||||
else
|
||||
if(irq_in != 0)
|
||||
irq_status <= irq_status | (irq_in & ~irq_mask); // add active irq to irq_status
|
||||
end
|
||||
|
||||
// irq mask
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (cs && wr_en && irq_wr_seten) // when enabling interrupts, clear mask
|
||||
irq_mask <= 0;
|
||||
else
|
||||
if(irq_out) // when signalling an interrupt, set mask from status
|
||||
irq_mask <= irq_status;
|
||||
end
|
||||
|
||||
// manage irq_enabled and irq_delay/irq_delaying
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(cs && wr_en) // when writing to control register
|
||||
begin
|
||||
if(irq_wr_seten) // if wr_seten flag is set, enable interrupts and start delay
|
||||
begin
|
||||
irq_enabled <= 1;
|
||||
irq_delaying <= 1;
|
||||
irq_delay <= 1;
|
||||
end
|
||||
else
|
||||
irq_enabled <= 0; // else disable interrupts
|
||||
end
|
||||
else if(irq_out) irq_enabled <= 0; // after sending interrupt to cpu, disable further interrupts
|
||||
|
||||
if(irq_delaying) // the delay gives the CPU a chance to return from an interrupt handler
|
||||
begin // if an interrupt is triggered again right after re-enabling interrupts
|
||||
if(irq_delay==0)
|
||||
begin
|
||||
irq_delay <= 1;
|
||||
irq_delaying <= 0;
|
||||
end
|
||||
else
|
||||
irq_delay <= irq_delay + 1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
161
rtl/src/mem.v
Normal file
161
rtl/src/mem.v
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company:
|
||||
// Engineer:
|
||||
//
|
||||
// Create Date: 05.01.2021 21:53:41
|
||||
// Design Name:
|
||||
// Module Name: mem
|
||||
// Project Name:
|
||||
// Target Devices:
|
||||
// Tool Versions:
|
||||
// Description:
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// 32 bit wide rom with byte addressing (address bits 1-0 are ignored)
|
||||
module rom32 #(parameter ADDR_WIDTH = 11, DATA_WIDTH = 32)
|
||||
(
|
||||
input wire clk,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
output reg [DATA_WIDTH-1:0] data_out,
|
||||
input wire read_enable
|
||||
);
|
||||
|
||||
wire [ADDR_WIDTH-2:0] internal_addr = addr[ADDR_WIDTH-1:2]; // -> ignore bit 0
|
||||
reg [DATA_WIDTH-1:0] rom [0:(2**(ADDR_WIDTH-2))-1];
|
||||
|
||||
initial begin
|
||||
$readmemb("C:\\Users\\sebastian\\develop\\fpga\\vdumbcpu\\rom.mem", rom);
|
||||
end
|
||||
|
||||
always @(posedge clk) data_out <= rom[internal_addr];
|
||||
|
||||
endmodule
|
||||
|
||||
module ram32 #(parameter ADDR_WIDTH = 16, DATA_WIDTH = 32)
|
||||
|
||||
(
|
||||
input wire clk,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
output reg [DATA_WIDTH-1:0] data_out,
|
||||
input wire read_enable,
|
||||
input wire [DATA_WIDTH-1:0] data_in,
|
||||
input wire write_enable
|
||||
);
|
||||
|
||||
reg [DATA_WIDTH-1:0] ram [0:(2**(ADDR_WIDTH-2))-1]; // 32bit words with byte addressing
|
||||
wire [ADDR_WIDTH-2:0] internal_addr = addr[ADDR_WIDTH-1:2]; // -> ignore bit 1-0
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(read_enable)
|
||||
data_out <= ram[internal_addr];
|
||||
if(write_enable)
|
||||
ram[internal_addr] <= data_in;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module mem #(parameter ADDR_WIDTH = 32,
|
||||
parameter DATA_WIDTH = 32)
|
||||
(
|
||||
input wire clk, rst_n,
|
||||
input wire [ADDR_WIDTH-1:0] addr,
|
||||
output wire [DATA_WIDTH-1:0] data_out,
|
||||
input wire read_enable,
|
||||
input wire [DATA_WIDTH-1:0] data_in,
|
||||
input wire write_enable,
|
||||
output wire io_enable,
|
||||
input wire [DATA_WIDTH-1:0] io_rd_data,
|
||||
output wire mem_wait,
|
||||
|
||||
output wire [ADDR_WIDTH-1:0] dram_addr,
|
||||
input wire [DATA_WIDTH-1:0] dram_read_data,
|
||||
output wire [DATA_WIDTH-1:0] dram_write_data,
|
||||
output wire dram_read_enable,
|
||||
output wire dram_write_enable,
|
||||
input wire dram_wait
|
||||
);
|
||||
|
||||
wire [DATA_WIDTH-1:0] ram_out, rom_out, dram_out;
|
||||
|
||||
// address map:
|
||||
// ROM $0000 - $07FF 2K
|
||||
// IO $0800 - $0FFF 2K
|
||||
// RAM1 $1000 - $FFFF 60K
|
||||
// RAM2 $10000 - $FFFFFFFF ~4GB
|
||||
|
||||
wire ram_cs = addr[ADDR_WIDTH-1:12] != { {(ADDR_WIDTH-12){1'b0}}};
|
||||
wire ram1_cs = ram_cs && (addr[ADDR_WIDTH-1:16] == { {(ADDR_WIDTH-16){1'b0}}});
|
||||
wire ram2_cs = ram_cs && !ram1_cs;
|
||||
wire rom_cs = !ram_cs && addr[11] == 1'b0;
|
||||
wire io_cs = !ram_cs && addr[11] == 1'b1;
|
||||
|
||||
assign io_enable = io_cs;
|
||||
|
||||
wire ram_read = ram1_cs && read_enable;
|
||||
wire ram_write = ram1_cs && write_enable;
|
||||
wire rom_read = rom_cs && read_enable;
|
||||
|
||||
reg [DATA_WIDTH-1:0] data_buf;
|
||||
|
||||
localparam SEL_RAM1 = 0;
|
||||
localparam SEL_RAM2 = 1;
|
||||
localparam SEL_ROM = 2;
|
||||
localparam SEL_IO = 3;
|
||||
localparam SEL_ERR = 4;
|
||||
|
||||
reg [1:0] out_sel;
|
||||
|
||||
// test
|
||||
reg [1:0] wait_state;
|
||||
|
||||
ram32 #(.ADDR_WIDTH(16)) ram0 // 64KB RAM
|
||||
(
|
||||
.clk(clk),
|
||||
.addr(addr[15:0]),
|
||||
.data_out(ram_out),
|
||||
.read_enable(ram_read),
|
||||
.data_in(data_in),
|
||||
.write_enable(ram_write)
|
||||
);
|
||||
|
||||
rom32 #(.ADDR_WIDTH(11)) rom0 // 2KB ROM
|
||||
(
|
||||
.clk(clk),
|
||||
.addr(addr[10:0]),
|
||||
.data_out(rom_out),
|
||||
.read_enable(rom_read)
|
||||
);
|
||||
|
||||
assign dram_out = dram_read_data;
|
||||
assign dram_addr = addr;
|
||||
assign dram_write_data = data_in;
|
||||
assign dram_read_enable = ram2_cs & read_enable;
|
||||
assign dram_write_enable = ram2_cs & write_enable;
|
||||
|
||||
assign data_out = (out_sel == SEL_RAM1 ) ? ram_out :
|
||||
(out_sel == SEL_RAM2 ) ? dram_out :
|
||||
(out_sel == SEL_ROM ) ? rom_out :
|
||||
(out_sel == SEL_IO ) ? io_rd_data :
|
||||
data_buf;
|
||||
|
||||
assign mem_wait = ram2_cs && dram_wait;
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
data_buf <= data_out;
|
||||
if(read_enable) out_sel <=
|
||||
ram1_cs ? SEL_RAM1 :
|
||||
ram2_cs ? SEL_RAM2:
|
||||
rom_cs ? SEL_ROM :
|
||||
io_cs ? SEL_IO :
|
||||
SEL_ERR;
|
||||
end
|
||||
endmodule
|
||||
28
rtl/src/palette.v
Normal file
28
rtl/src/palette.v
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
`timescale 1ns / 1ps
|
||||
// taken from https://danstrother.com/2010/09/11/inferring-rams-in-fpgas/
|
||||
// modified for one read/write-port and one read-only-port,
|
||||
/// A parameterized, inferable, true dual-port, dual-clock block RAM in Verilog.
|
||||
|
||||
module palette #(
|
||||
parameter SLOTS_WIDTH = 4, COLOR_WIDTH = 12
|
||||
) (
|
||||
input wire wr_clk,
|
||||
input wire rd_clk,
|
||||
input wire wr_en,
|
||||
input wire [SLOTS_WIDTH-1:0] wr_slot,
|
||||
input wire [COLOR_WIDTH-1:0] wr_data,
|
||||
input wire [SLOTS_WIDTH-1:0] rd_slot,
|
||||
output wire [COLOR_WIDTH-1:0] rd_data
|
||||
);
|
||||
|
||||
// Shared memory
|
||||
reg [COLOR_WIDTH-1:0] colors [(2**SLOTS_WIDTH)-1:0];
|
||||
|
||||
assign rd_data = colors[rd_slot];
|
||||
|
||||
always @(posedge wr_clk) begin
|
||||
if(wr_en) colors[wr_slot] <= wr_data;
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
372
rtl/src/sdspi.v
Normal file
372
rtl/src/sdspi.v
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
`timescale 1ns / 1ps
|
||||
|
||||
|
||||
// Every spi_clk_div cpu clock cycles the spi clock line is inverted.
|
||||
// So a spi clock cycle is 2*spi_clk_div cpu clock cycles.
|
||||
// spi_clk_count counts the cpu cycles from spi_clk_div down to zero.
|
||||
// The resulting spi clock frequency is:
|
||||
// (cpu clock freq) / ((sclk_count + 1) * 2)
|
||||
// So for a 83.33 MHz cpu clock, we get
|
||||
// spi_clk_div = 10: 83.333 / 22 = 3.788 MHz
|
||||
// spi_clk_div = 124: 83.333 / 250 = 333.33 KHz
|
||||
|
||||
|
||||
module sdspi(
|
||||
input wire clk, // bus clock
|
||||
input wire reset,
|
||||
|
||||
input wire[7:0] tx_data, // data to transmit
|
||||
output wire[7:0] rx_data, // data received
|
||||
output wire tx_ready, // ready to write a data byte
|
||||
output wire tx_empty, // transmitter fifo is empty
|
||||
output wire rx_avail, // a byte has been received
|
||||
output wire rx_ovr, // receiver overrun
|
||||
input wire tx_write, // write strobe
|
||||
input wire rx_read, // read strobe (clears rx_ovr)
|
||||
|
||||
output wire card_detect, // true is card is present
|
||||
output wire card_changed, // card_detect signal has changed
|
||||
output wire card_busy, // card is busy (MISO/DO is 0)
|
||||
|
||||
input wire ctrl_write, // set the following flags
|
||||
input wire rx_filter_en, // set to discard received $FF bytes
|
||||
input wire txrx_en, // enable transmitter and receiver
|
||||
input wire spiclk_f_en, // enable spi clock without cs
|
||||
input wire spiclk_div_wr, // set clock divider via tx_data
|
||||
|
||||
// PMOD connections
|
||||
output wire sd_cs_n,
|
||||
output reg sd_mosi,
|
||||
input wire sd_miso,
|
||||
output wire sd_sck,
|
||||
input wire sd_cd
|
||||
);
|
||||
|
||||
localparam CLKPHASE_0A = 2'b00;
|
||||
localparam CLKPHASE_0B = 2'b01;
|
||||
localparam CLKPHASE_1A = 2'b10;
|
||||
localparam CLKPHASE_1B = 2'b11;
|
||||
reg [1:0] clk_phase;
|
||||
|
||||
reg xcvr_on; // if turned off, the rest of the current byte
|
||||
// will still transmitted, and until then "running"
|
||||
// will be 1
|
||||
(* KEEP *)
|
||||
reg running; // transmitting/receiving a byte (maybe a dummy byte)
|
||||
|
||||
(* KEEP *) reg [3:0] xcvr_bitcount; // number of bits left of the current byte
|
||||
|
||||
reg [7:0] tx_shifter;
|
||||
wire tx_fifo_wr_en;
|
||||
reg tx_fifo_rd_en;
|
||||
wire tx_fifo_full;
|
||||
wire tx_fifo_empty;
|
||||
wire [7:0] tx_fifo_out;
|
||||
|
||||
reg [7:0] rx_shifter;
|
||||
reg rx_filter;
|
||||
reg rx_fifo_wr_en;
|
||||
wire rx_fifo_rd_en;
|
||||
wire rx_fifo_full;
|
||||
wire rx_fifo_empty;
|
||||
wire [7:0] rx_fifo_out;
|
||||
|
||||
reg rx_bit_recvd; // this flag signals a received bit
|
||||
|
||||
reg rx_overrun; // byte received when rx fifo is full
|
||||
|
||||
reg c_changed;
|
||||
reg c_cs;
|
||||
|
||||
reg spi_clk; // the spi clock signal
|
||||
reg spi_clk_on; // enable clock, either via init mode or by xcvr_on
|
||||
reg spi_clk_f_on; // init clock mode, i.e. start clock but no tx/rx
|
||||
reg [6:0] spi_clk_count; // counting cpu clock ticks
|
||||
reg [6:0] spi_clk_div; // tick counter for spi clock phases
|
||||
wire spi_clk_count_z = (spi_clk_count == 7'b0);
|
||||
reg hphase_start; // start of a spi clock half-phase
|
||||
|
||||
assign tx_ready = !tx_fifo_full;
|
||||
assign tx_empty = tx_fifo_empty;
|
||||
assign rx_avail = !rx_fifo_empty;
|
||||
assign rx_ovr = rx_overrun;
|
||||
assign rx_data = rx_fifo_out;
|
||||
|
||||
assign card_busy = (sd_miso == 0);
|
||||
assign card_changed = c_changed;
|
||||
|
||||
assign sd_sck = spi_clk;
|
||||
assign sd_cs_n = ~c_cs;
|
||||
|
||||
assign card_detect = sd_cd;
|
||||
|
||||
fifo #(.ADDR_WIDTH(4)) tx_fifo(clk, reset,
|
||||
tx_fifo_wr_en, tx_fifo_rd_en,
|
||||
tx_data, tx_fifo_out,
|
||||
tx_fifo_full,
|
||||
tx_fifo_empty
|
||||
);
|
||||
|
||||
fifo #(.ADDR_WIDTH(8)) rx_fifo(clk, reset,
|
||||
rx_fifo_wr_en, rx_fifo_rd_en,
|
||||
rx_shifter, rx_fifo_out,
|
||||
rx_fifo_full,
|
||||
rx_fifo_empty
|
||||
);
|
||||
|
||||
// spi clock
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
spi_clk <= 1; // CLK is high when inactive
|
||||
spi_clk_on <= 0;
|
||||
spi_clk_count <= 0;
|
||||
end
|
||||
else if(spi_clk_on)
|
||||
begin
|
||||
|
||||
// set spi_clk at start of every half-phase
|
||||
if(hphase_start)
|
||||
case(clk_phase)
|
||||
CLKPHASE_0A: spi_clk <= 1'b0;
|
||||
CLKPHASE_0B: spi_clk <= 1'b0;
|
||||
CLKPHASE_1A: spi_clk <= 1'b1;
|
||||
CLKPHASE_1B: spi_clk <= 1'b1;
|
||||
endcase
|
||||
|
||||
if(spi_clk_count_z)
|
||||
begin
|
||||
spi_clk_count <= spi_clk_div;
|
||||
clk_phase <= clk_phase + 2'b1;
|
||||
end
|
||||
else
|
||||
spi_clk_count <= spi_clk_count - 7'd1;
|
||||
end
|
||||
|
||||
// start the clock if needed
|
||||
if( (spi_clk_on == 0) && (running || spi_clk_f_on))
|
||||
begin
|
||||
spi_clk_on <= 1;
|
||||
spi_clk_count <= spi_clk_div;
|
||||
clk_phase <= CLKPHASE_1A;
|
||||
end
|
||||
|
||||
// turn off the clock if transceiver not running
|
||||
// and the force-clock-on flag is not set
|
||||
if( (spi_clk_on == 1) && (!running && !spi_clk_f_on))
|
||||
begin
|
||||
spi_clk_on <= 0;
|
||||
spi_clk <= 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
// half-phase-start flag trails spi_clk_count_z by one tick
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
hphase_start <= 0;
|
||||
else
|
||||
hphase_start <= spi_clk_on && spi_clk_count_z;
|
||||
end
|
||||
|
||||
// handle the force clock enable flag
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
spi_clk_f_on <= 0;
|
||||
else
|
||||
if (ctrl_write)
|
||||
spi_clk_f_on <= spiclk_f_en;
|
||||
end
|
||||
|
||||
// clock divider
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (spiclk_div_wr) spi_clk_div <= tx_data[6:0];
|
||||
end
|
||||
|
||||
// card_changed flag
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(sd_cd)
|
||||
c_changed <= 1;
|
||||
else if(ctrl_write || reset)
|
||||
c_changed <= 0;
|
||||
end
|
||||
|
||||
// cs signal
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(hphase_start && clk_phase == CLKPHASE_0A || !running)
|
||||
c_cs <= running;
|
||||
end
|
||||
|
||||
// transmitter
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
// ???? we start the bitcount at 1 because we start
|
||||
// at the second clock phase where the bitcount
|
||||
// is decremented and the next byte gets loaded
|
||||
xcvr_bitcount <= 0;
|
||||
|
||||
tx_shifter <= 8'b1;
|
||||
xcvr_on <= 0;
|
||||
sd_mosi <= 1;
|
||||
tx_fifo_rd_en <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// handle a control write to disable the transceiver
|
||||
if(ctrl_write && !txrx_en && xcvr_on)
|
||||
xcvr_on <= 0;
|
||||
// a byte might still be in transit, so
|
||||
// we do not disable the transceiver
|
||||
// immediately (see "handle running status" below)
|
||||
else
|
||||
// handle control write to enable the transceiver
|
||||
if(ctrl_write && txrx_en && !running)
|
||||
begin
|
||||
xcvr_on <= 1;
|
||||
xcvr_bitcount <= 0;
|
||||
// next clock phase must be 1B when starting the transceiver,
|
||||
// so that the first byte is loaded into the shifter then
|
||||
tx_shifter <= 8'b11111111;
|
||||
// in case the transceiver is enabled, but no data is in the fifo,
|
||||
// initialize the shifter with $FF
|
||||
end
|
||||
else
|
||||
// handle clock phases
|
||||
if (running)
|
||||
begin
|
||||
if(hphase_start)
|
||||
case(clk_phase)
|
||||
// set mosi signal at start of clock pulse
|
||||
CLKPHASE_0A: sd_mosi <= tx_shifter[7];
|
||||
CLKPHASE_0B: ;
|
||||
CLKPHASE_1A: begin // shift at rising clock
|
||||
tx_shifter <= tx_shifter << 1;
|
||||
xcvr_bitcount <= xcvr_bitcount - 1;
|
||||
end
|
||||
CLKPHASE_1B: begin // in the middle of the high clock pulse,
|
||||
// fetch the next byte if there are no bits
|
||||
// left in the shift register
|
||||
if (xcvr_bitcount == 0)
|
||||
begin
|
||||
if(!tx_fifo_empty)
|
||||
begin
|
||||
tx_shifter <= tx_fifo_out;
|
||||
tx_fifo_rd_en <= 1;
|
||||
end
|
||||
else
|
||||
tx_shifter <= 8'b11111111;
|
||||
xcvr_bitcount <= 8;
|
||||
end
|
||||
else
|
||||
tx_fifo_rd_en <= 0;
|
||||
end
|
||||
endcase
|
||||
else
|
||||
tx_fifo_rd_en <= 0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// handle data write
|
||||
assign tx_fifo_wr_en = tx_write && !tx_fifo_full;
|
||||
|
||||
// Enable fifo read signal if fifo is not empty.
|
||||
// The data at the fifo tail is always available at
|
||||
// rx_data, the read signal just moves the tail pointer
|
||||
// forward.
|
||||
assign rx_fifo_rd_en = rx_read && !rx_fifo_empty;
|
||||
|
||||
// receiver
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
rx_bit_recvd <= 0;
|
||||
rx_shifter <= 8'b11111111;
|
||||
rx_fifo_wr_en <= 0;
|
||||
rx_filter <= 0;
|
||||
rx_overrun <= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
// handle a control write
|
||||
if(ctrl_write)
|
||||
begin
|
||||
rx_filter <= rx_filter_en;
|
||||
rx_overrun <= 0;
|
||||
if(txrx_en && !running)
|
||||
rx_shifter <= 8'b0;
|
||||
end
|
||||
|
||||
if (running && hphase_start)
|
||||
case(clk_phase)
|
||||
CLKPHASE_0A: ;
|
||||
CLKPHASE_0B: ;
|
||||
CLKPHASE_1A: ;
|
||||
CLKPHASE_1B: begin // in the middle of the high clock pulse,
|
||||
// sample MISO and put into shift register
|
||||
// and shift at the same time
|
||||
rx_shifter <= { rx_shifter[6:0],sd_miso};
|
||||
rx_bit_recvd <= 1;
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rx_bit_recvd && !sd_cs_n && clk_phase == CLKPHASE_1B)
|
||||
begin
|
||||
rx_bit_recvd <= 0;
|
||||
|
||||
// if a complete byte was received, bitcount will be
|
||||
// 8 because the transmitter has already loaded the next byte
|
||||
// at this half-phase
|
||||
if (xcvr_bitcount == 8)
|
||||
begin
|
||||
// discard $FF bytes if filter is enabled
|
||||
if(!rx_filter || rx_shifter != 8'b11111111)
|
||||
begin
|
||||
if(rx_fifo_full) // discard received byte if fifo is full
|
||||
rx_overrun <= 1; // and set overrun flag
|
||||
else
|
||||
rx_fifo_wr_en <= 1; // otherwise, enable fifo write strobe,
|
||||
// fifo will take data from rx_shifter
|
||||
end
|
||||
|
||||
// turn off filter if a byte != $FF was received
|
||||
if (rx_filter && rx_shifter != 8'b11111111)
|
||||
rx_filter <= 0;
|
||||
end
|
||||
end
|
||||
else
|
||||
rx_fifo_wr_en <= 0;
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// handle running status
|
||||
// (especially keep transmitter running when there are still bits left to be
|
||||
// transmitted)
|
||||
always@(posedge clk)
|
||||
begin
|
||||
if (reset)
|
||||
running <= 0;
|
||||
else
|
||||
begin
|
||||
// if we want to turn the transceiver on, set running flag
|
||||
if (!running && xcvr_on)
|
||||
running <= 1;
|
||||
|
||||
// when running and a byte has been transmitted,
|
||||
// check if we should turn the transceiver off
|
||||
if (running && hphase_start && xcvr_bitcount==0)
|
||||
if(clk_phase == CLKPHASE_1B)
|
||||
if (!xcvr_on)
|
||||
running <= 0;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
199
rtl/src/sdspi_testbench.v
Normal file
199
rtl/src/sdspi_testbench.v
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
`timescale 1ns / 1ns
|
||||
`default_nettype none
|
||||
|
||||
module sdspi_testbench();
|
||||
parameter CLOCK_NS = 10;
|
||||
integer i,j;
|
||||
|
||||
reg[7:0] rx_testdata[0:3];
|
||||
reg[7:0] read_data;
|
||||
|
||||
// Test signals
|
||||
reg clk = 0;
|
||||
reg reset = 0;
|
||||
|
||||
reg[7:0] tx_data = 0;
|
||||
wire[7:0] rx_data;
|
||||
wire tx_ready;
|
||||
wire tx_empty;
|
||||
wire rx_avail;
|
||||
wire rx_ovr;
|
||||
reg tx_write = 0;
|
||||
reg rx_read = 0;
|
||||
|
||||
wire card_detect;
|
||||
wire card_changed;
|
||||
wire card_busy;
|
||||
|
||||
reg ctrl_write = 0;
|
||||
reg rx_filter_en = 0;
|
||||
reg txrx_en = 0;
|
||||
reg spiclk_f_en = 0;
|
||||
reg spiclk_div_wr = 0;
|
||||
|
||||
// PMOD connections
|
||||
wire sd_cs_n;
|
||||
wire sd_mosi;
|
||||
reg sd_miso = 1;
|
||||
wire sd_sck;
|
||||
reg sd_cd = 1;
|
||||
|
||||
// Unit Under Test
|
||||
sdspi UUT (
|
||||
.clk(clk),
|
||||
.reset(reset),
|
||||
.tx_data(tx_data),
|
||||
.rx_data(rx_data),
|
||||
.tx_ready(tx_ready),
|
||||
.tx_empty(tx_empty),
|
||||
.rx_avail(rx_avail),
|
||||
.rx_ovr(rx_ovr),
|
||||
.tx_write(tx_write),
|
||||
.rx_read(rx_read),
|
||||
.card_detect(card_detect),
|
||||
.card_changed(card_changed),
|
||||
.card_busy(card_busy),
|
||||
.ctrl_write(ctrl_write),
|
||||
.rx_filter_en(rx_filter_en),
|
||||
.txrx_en(txrx_en),
|
||||
.spiclk_f_en(spiclk_f_en),
|
||||
.spiclk_div_wr(spiclk_div_wr),
|
||||
.sd_cs_n(sd_cs_n),
|
||||
.sd_mosi(sd_mosi),
|
||||
.sd_miso(sd_miso),
|
||||
.sd_sck(sd_sck),
|
||||
.sd_cd(sd_cd)
|
||||
);
|
||||
|
||||
// testbench clock
|
||||
always
|
||||
#(CLOCK_NS/2) clk <= ~clk;
|
||||
|
||||
initial
|
||||
begin
|
||||
rx_testdata[0] <= 'hFF;
|
||||
rx_testdata[1] <= 'hFF;
|
||||
rx_testdata[2] <= 'hCA;
|
||||
rx_testdata[3] <= 'hFE;
|
||||
|
||||
// issue reset
|
||||
reset = 1'b1;
|
||||
#10
|
||||
reset = 1'b0;
|
||||
#10
|
||||
|
||||
// set clock divider
|
||||
tx_data <= 3;
|
||||
spiclk_div_wr <= 1'b1;
|
||||
#10
|
||||
spiclk_div_wr <= 1'b0;
|
||||
#10
|
||||
|
||||
// Card initialization phase,
|
||||
// cycle the clock at least 74 times
|
||||
// while cs is off and mosi is high .
|
||||
// we do 32 cycles
|
||||
spiclk_f_en <= 1'b1;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
spiclk_f_en <= 1'b0;
|
||||
ctrl_write <= 1'b0;
|
||||
#10
|
||||
for (i=0; i<32*16; i = i + 1)
|
||||
#10;
|
||||
spiclk_f_en <= 1'b0;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
ctrl_write <= 1'b0;
|
||||
#10
|
||||
for (i=0; i<32*16; i = i + 1)
|
||||
#10;
|
||||
|
||||
// Write two bytes
|
||||
tx_data <= 8'hAB;
|
||||
tx_write <= 1'b1;
|
||||
#10;
|
||||
tx_data <= 8'hCD;
|
||||
tx_write <= 1'b1;
|
||||
#10;
|
||||
tx_write <= 1'b0;
|
||||
#10
|
||||
|
||||
// start transceiver, enable rx_filter
|
||||
txrx_en <= 1'b1;
|
||||
rx_filter_en <= 1'b1;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
txrx_en <= 1'b0;
|
||||
rx_filter_en <= 1'b0;
|
||||
ctrl_write <= 1'b0;
|
||||
|
||||
for (i = 0; i < 2048; i = i + 1)
|
||||
begin
|
||||
if (!sd_cs_n && (i % 16)==15) sd_miso <= rx_testdata[(i/(16*8)) % 4][7 - (i/16 % 8)];
|
||||
#10;
|
||||
end
|
||||
tx_write <= 1'b0;
|
||||
#10
|
||||
|
||||
// read from rx fifo
|
||||
read_data <= rx_data;
|
||||
#10
|
||||
$display("read data 1: %02h", read_data);
|
||||
|
||||
|
||||
// strobe rx_read to go to next byte
|
||||
rx_read <= 1'b1;
|
||||
#10 // one cycle to transfer the data
|
||||
rx_read <= 1'b0;
|
||||
#10 // we need this extra cycle for the fifo tail to move
|
||||
|
||||
read_data <= rx_data;
|
||||
#10
|
||||
$display("read data 2: %02h", read_data);
|
||||
|
||||
// strobe rx_read to go to next byte
|
||||
rx_read <= 1'b1;
|
||||
#10
|
||||
rx_read <= 1'b0;
|
||||
#10 // we need this extra cycle for the fifo tail to move
|
||||
|
||||
read_data <= rx_data;
|
||||
#10
|
||||
$display("read data 3: %02h", read_data);
|
||||
|
||||
|
||||
// set flag to turn transceiver off
|
||||
txrx_en <= 1'b0;
|
||||
ctrl_write <= 1'b1;
|
||||
#10
|
||||
ctrl_write <= 1'b0;
|
||||
#10;
|
||||
|
||||
// wait for the transceiver to actually turn off
|
||||
for (i=0; i<32*16; i = i + 1)
|
||||
#10;
|
||||
|
||||
#10
|
||||
// clear rx fifo
|
||||
for(i=0; i<14; i = i + 1)
|
||||
begin
|
||||
$display("clear fifo data %02h", rx_data);
|
||||
rx_read <= 1'b1;
|
||||
#10
|
||||
rx_read <= 1'b0;
|
||||
#10
|
||||
#10
|
||||
#10; // simulate the four cycles of an instruction
|
||||
end
|
||||
|
||||
$finish();
|
||||
end
|
||||
|
||||
initial
|
||||
begin
|
||||
// Required to dump signals
|
||||
$dumpfile("sdspi_tb_dump.vcd");
|
||||
$dumpvars(0);
|
||||
end
|
||||
endmodule
|
||||
42
rtl/src/stack.v
Normal file
42
rtl/src/stack.v
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Company:
|
||||
// Engineer:
|
||||
//
|
||||
// Create Date: 17.01.2021 20:59:29
|
||||
// Design Name:
|
||||
// Module Name: stack
|
||||
// Project Name:
|
||||
// Target Devices:
|
||||
// Tool Versions:
|
||||
// Description:
|
||||
//
|
||||
// Dependencies:
|
||||
//
|
||||
// Revision:
|
||||
// Revision 0.01 - File Created
|
||||
// Additional Comments:
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
module stack
|
||||
#(parameter ADDR_WIDTH=4, DATA_WIDTH=16)
|
||||
(
|
||||
input wire clk,
|
||||
input wire [ADDR_WIDTH-1:0] rd_addr,
|
||||
input wire [ADDR_WIDTH-1:0] wr_addr,
|
||||
input wire wr_enable,
|
||||
output wire [DATA_WIDTH-1:0] rd_data,
|
||||
input wire [DATA_WIDTH-1:0] wr_data
|
||||
);
|
||||
|
||||
reg [DATA_WIDTH-1:0] stack[0:2**ADDR_WIDTH-1];
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(wr_enable) stack[wr_addr] <= wr_data;
|
||||
end
|
||||
|
||||
assign rd_data = stack[rd_addr];
|
||||
endmodule
|
||||
427
rtl/src/stackcpu.v
Normal file
427
rtl/src/stackcpu.v
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
`timescale 1ns / 1ps
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32,
|
||||
WORDSIZE = 4, WORDSIZE_SHIFT = 2) (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
input wire irq,
|
||||
|
||||
output reg [ADDR_WIDTH-1:0] addr,
|
||||
input wire [WIDTH-1:0] data_in,
|
||||
output wire read_enable,
|
||||
output wire [WIDTH-1:0] data_out,
|
||||
output wire write_enable,
|
||||
input wire mem_wait,
|
||||
|
||||
output wire led1,
|
||||
output wire led2,
|
||||
output wire led3,
|
||||
|
||||
output wire [WIDTH-1:0] debug_out1,
|
||||
output wire [WIDTH-1:0] debug_out2,
|
||||
output wire [WIDTH-1:0] debug_out3,
|
||||
output wire [WIDTH-1:0] debug_out4,
|
||||
output wire [WIDTH-1:0] debug_out5,
|
||||
output wire [WIDTH-1:0] debug_out6
|
||||
);
|
||||
|
||||
localparam EVAL_STACK_INDEX_WIDTH = 6;
|
||||
|
||||
wire reset = !rst;
|
||||
|
||||
(* KEEP *) reg [1:0] seq_state;
|
||||
localparam FETCH = 2'b00; localparam DECODE = 2'b01; localparam EXEC = 2'b10; localparam MEM = 2'b11;
|
||||
|
||||
(* KEEP*) reg [WIDTH-1:0] X, nX;
|
||||
wire [WIDTH-1:0] Y;
|
||||
(* KEEP *) reg [WIDTH-1:0] PC, nPC;
|
||||
reg [WIDTH-1:0] RP, nRP;
|
||||
reg [WIDTH-1:0] FP, BP;
|
||||
reg [WIDTH-1:0] IV,IR;
|
||||
|
||||
wire [WIDTH-1:0] pc_next_ins = PC + 2;
|
||||
|
||||
reg [EVAL_STACK_INDEX_WIDTH-1:0] ESP, nESP;
|
||||
reg stack_write;
|
||||
|
||||
reg irq_pending;
|
||||
|
||||
// eval stack
|
||||
stack #(.ADDR_WIDTH(EVAL_STACK_INDEX_WIDTH), .DATA_WIDTH(WIDTH)) estack (
|
||||
.clk(clk),
|
||||
.rd_addr(ESP),
|
||||
.wr_addr(nESP),
|
||||
.wr_enable(stack_write),
|
||||
.rd_data(Y),
|
||||
.wr_data(X)
|
||||
);
|
||||
|
||||
reg [15:0] ins;
|
||||
|
||||
wire [WIDTH-1:0] operand;
|
||||
|
||||
// decoded instructions
|
||||
wire ins_loadrel;
|
||||
wire ins_load;
|
||||
wire ins_loadc;
|
||||
wire ins_store;
|
||||
wire ins_aluop;
|
||||
wire ins_ext;
|
||||
wire ins_xfer;
|
||||
wire ins_branch;
|
||||
wire ins_cbranch;
|
||||
|
||||
// decoded extended instructions
|
||||
wire ins_mem, ins_loadi, ins_storei;
|
||||
wire ins_fpadj;
|
||||
wire ins_reg, ins_loadreg, ins_storereg;
|
||||
wire ins_reg_fp, ins_reg_bp, ins_reg_rp;
|
||||
wire ins_reg_iv, ins_reg_ir;
|
||||
wire ins_reg_esp;
|
||||
wire loadstore_base;
|
||||
wire cbranch_n;
|
||||
|
||||
wire xfer_x2p, xfer_r2p, xfer_p2r;
|
||||
wire [1:0] xfer_rs;
|
||||
|
||||
wire [3:0] aluop;
|
||||
wire [1:0] aluop_sd;
|
||||
wire aluop_x2y, aluop_ext;
|
||||
|
||||
wire cmp_i, cmp_e, cmp_l;
|
||||
|
||||
wire mem_read;
|
||||
wire mem_write;
|
||||
|
||||
wire x_is_zero;
|
||||
// wire [WIDTH-1:0] y_plus_operand = Y + operand;
|
||||
|
||||
wire x_equals_y = X == Y;
|
||||
wire y_lessthan_x = $signed(Y) < $signed(X);
|
||||
wire yx_unsigned_less = Y < X;
|
||||
|
||||
reg [WIDTH-1:0] mem_write_data;
|
||||
|
||||
wire mem_read_enable, mem_write_enable;
|
||||
|
||||
assign read_enable = mem_read_enable;
|
||||
assign data_out = mem_write_data;
|
||||
assign write_enable = mem_write_enable;
|
||||
|
||||
// debug output ------------------------------------------------------------------------------------
|
||||
assign led1 = reset;
|
||||
assign led2 = ins_loadc;
|
||||
assign led3 = ins_branch;
|
||||
// assign debug_out1 = { mem_read_enable, mem_write_enable, x_is_zero,
|
||||
// ins_branch, ins_aluop, y_lessthan_x, x_equals_y, {7{1'b0}}, seq_state};
|
||||
// assign debug_out2 = data_in;
|
||||
// assign debug_out3 = nX;
|
||||
// assign debug_out4 = nPC;
|
||||
// assign debug_out5 = ins;
|
||||
// assign debug_out6 = IV;
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
// instruction decoding
|
||||
assign ins_branch = (ins[15:13] == 3'b000);
|
||||
assign ins_aluop = (ins[15:13] == 3'b001);
|
||||
assign ins_store = (ins[15:13] == 3'b010);
|
||||
assign ins_xfer = (ins[15:13] == 3'b011);
|
||||
assign ins_load = (ins[15:13] == 3'b100);
|
||||
assign ins_cbranch = (ins[15:13] == 3'b101);
|
||||
assign ins_loadc = (ins[15:13] == 3'b110);
|
||||
assign ins_ext = (ins[15:13] == 3'b111);
|
||||
|
||||
// sub-decode LOAD/STORE
|
||||
assign loadstore_base = ins[0];
|
||||
|
||||
// sub-decode CBRANCH
|
||||
assign cbranch_n = ins[0];
|
||||
|
||||
// sub-decode XFER
|
||||
assign xfer_x2p = ins[0];
|
||||
assign xfer_r2p = ins[7];
|
||||
assign xfer_p2r = ins[6];
|
||||
assign xfer_rs = ins[9:8];
|
||||
|
||||
// sub-decode OP
|
||||
assign aluop = ins[12:9];
|
||||
assign aluop_x2y = ins[6];
|
||||
assign aluop_sd = ins[5:4];
|
||||
assign aluop_ext = ins[7];
|
||||
|
||||
// sub-decode OP.CMP
|
||||
assign cmp_i = ins[2];
|
||||
assign cmp_e = ins[1];
|
||||
assign cmp_l = ins[0];
|
||||
|
||||
assign x_is_zero = X == {WIDTH{1'b0}};
|
||||
|
||||
// decode extended instructions
|
||||
assign ins_reg = (ins_ext && ins[12:10] == 3'b000);
|
||||
assign ins_mem = (ins_ext && ins[12:10] == 3'b001);
|
||||
assign ins_loadi = (ins_mem && ins[9] == 1'b0);
|
||||
assign ins_storei = (ins_mem && ins[9] == 1'b1);
|
||||
assign ins_fpadj = (ins_ext && ins[12:10] == 3'b011);
|
||||
assign ins_loadrel= (ins_ext && ins[12:10] == 3'b101);
|
||||
|
||||
// sub-decode LOADREG/STOREREG
|
||||
assign ins_loadreg = (ins_reg && ins[9] == 1'b0);
|
||||
assign ins_storereg = (ins_reg && ins[9] == 1'b1);
|
||||
assign ins_reg_fp = (ins_reg && ins[3:0] == 4'b0000);
|
||||
assign ins_reg_bp = (ins_reg && ins[3:0] == 4'b0001);
|
||||
assign ins_reg_rp = (ins_reg && ins[3:0] == 4'b0010);
|
||||
assign ins_reg_iv = (ins_reg && ins[3:0] == 4'b0011);
|
||||
assign ins_reg_ir = (ins_reg && ins[3:0] == 4'b0100);
|
||||
assign ins_reg_esp = (ins_reg && ins[3:0] == 4'b0101);
|
||||
|
||||
assign mem_read = ins_loadi || ins_load || ins_loadrel || (ins_xfer && xfer_r2p);
|
||||
assign mem_write = ins_storei || ins_store || (ins_xfer && xfer_p2r);
|
||||
|
||||
assign mem_read_enable = (seq_state == FETCH) || (seq_state == EXEC && mem_read);
|
||||
assign mem_write_enable = (seq_state == MEM && mem_write);
|
||||
|
||||
initial
|
||||
begin
|
||||
PC <= 0; nPC <= 0; seq_state <= MEM;
|
||||
ESP <= -1; nESP <= -1;
|
||||
addr <= 0;
|
||||
FP <= 0; BP <= 0; RP <= 0; nRP <= 0;
|
||||
IV <= 0; IR <= 0;
|
||||
irq_pending <= 0;
|
||||
end
|
||||
|
||||
// instruction sequencer
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
seq_state <= MEM;
|
||||
else if(mem_wait == 1'b0)
|
||||
case(seq_state)
|
||||
FETCH: seq_state <= DECODE;
|
||||
DECODE: seq_state <= EXEC;
|
||||
EXEC: seq_state <= MEM;
|
||||
MEM: seq_state <= FETCH;
|
||||
default: seq_state <= FETCH;
|
||||
endcase
|
||||
end
|
||||
|
||||
// operand register
|
||||
assign operand =
|
||||
(ins_load || ins_store || ins_branch || ins_cbranch) ?
|
||||
{ {(WIDTH-13){ins[12]}}, ins[12:1], 1'b0 }
|
||||
: (ins_loadc) ? { {(WIDTH-13){ins[12]}}, ins[12:0] } // sign extend
|
||||
: (ins_aluop || ins_mem) ?
|
||||
{ {(WIDTH-4){1'b0}}, ins[3:0] }
|
||||
: (ins_loadrel) ? { {(WIDTH-10){1'b0}}, ins[9:0] }
|
||||
: (ins_fpadj) ? { {(WIDTH-10){ins[9]}}, ins[9:0] } // sign extend
|
||||
: { {WIDTH{1'b0}} };
|
||||
|
||||
// program counter
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset) nPC <= 0;
|
||||
else
|
||||
case(seq_state)
|
||||
EXEC:
|
||||
if(ins_xfer && xfer_x2p) nPC <= X;
|
||||
else if(ins_branch || (ins_cbranch && (x_is_zero != cbranch_n))) nPC <= PC + operand;
|
||||
else nPC <= pc_next_ins;
|
||||
MEM:
|
||||
if(ins_xfer && xfer_r2p) nPC <= data_in;
|
||||
else if(irq_pending) nPC <= IV;
|
||||
endcase
|
||||
end
|
||||
|
||||
// return stack pointer
|
||||
always @*
|
||||
begin
|
||||
if(seq_state == EXEC || seq_state == DECODE || seq_state == MEM)
|
||||
begin
|
||||
if (ins_xfer) nRP <= RP +
|
||||
({ {(ADDR_WIDTH-3){xfer_rs[1]}},xfer_rs} << WORDSIZE_SHIFT);
|
||||
// sign extend xfer_rs and multiply by word size
|
||||
else if (ins_storereg && ins_reg_rp) nRP <= X;
|
||||
else nRP <= RP;
|
||||
end
|
||||
else nRP <= nRP;
|
||||
end
|
||||
|
||||
// instruction fetch
|
||||
// depending on bit 1 of the PC, read either the upper or lower half word as an instruction
|
||||
always @* if(seq_state == DECODE) ins <= PC[1] ? data_in[15:0] : data_in[31:16];
|
||||
|
||||
// RAM read/write
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
begin
|
||||
addr <= 0;
|
||||
mem_write_data <= 0;
|
||||
end
|
||||
else
|
||||
case(seq_state)
|
||||
DECODE:
|
||||
if(ins_load || ins_store) // read from address in BP/FP + offset
|
||||
addr <= operand + ( loadstore_base ? BP: FP);
|
||||
else if (ins_loadi) // read from address in X
|
||||
addr <= X;
|
||||
else if (ins_storei) // write to address in Y
|
||||
addr <= Y;
|
||||
else if (ins_loadrel) // read from address next to current instruction
|
||||
addr <= PC + operand;
|
||||
else if (ins_xfer && xfer_r2p) // read from return stack
|
||||
addr <= RP; // use the current RP
|
||||
else if (ins_xfer && xfer_p2r) // write to return stack
|
||||
addr <= nRP; // use the new RP
|
||||
EXEC:
|
||||
begin
|
||||
if (ins_store)
|
||||
mem_write_data <= X;
|
||||
else if (ins_storei)
|
||||
mem_write_data <= X;
|
||||
else if (ins_xfer && xfer_p2r)
|
||||
mem_write_data <= pc_next_ins;
|
||||
else
|
||||
mem_write_data <= 0;
|
||||
end
|
||||
MEM:
|
||||
if(!mem_wait) // do not change the address if mem_wait is active
|
||||
begin
|
||||
if(ins_xfer && xfer_r2p) addr <= data_in; // on RET take addr for next instruction from the data we just read from mem
|
||||
else addr <= irq_pending ? IV : nPC; // prepare fetch cycle
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
// X/ToS-Register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset) nX <= 0;
|
||||
else
|
||||
case(seq_state)
|
||||
// default: nX <= X;
|
||||
FETCH, DECODE:;
|
||||
EXEC:
|
||||
if(ins_loadc) nX <= operand;
|
||||
else if(ins_cbranch || ins_store || ins_storereg || (ins_xfer && xfer_x2p)) nX <= Y;
|
||||
else if(ins_storei) nX <= Y + operand;
|
||||
else if(ins_loadreg && ins_reg_fp) nX <= FP;
|
||||
else if(ins_loadreg && ins_reg_bp) nX <= BP;
|
||||
else if(ins_loadreg && ins_reg_rp) nX <= RP;
|
||||
else if(ins_loadreg && ins_reg_iv) nX <= IV;
|
||||
else if(ins_loadreg && ins_reg_ir) nX <= IR;
|
||||
else if(ins_loadreg && ins_reg_esp) nX <= ESP;
|
||||
else if(ins_aluop)
|
||||
begin
|
||||
case(aluop)
|
||||
4'b0000: nX = X + Y; // ADD
|
||||
4'b0001: nX = Y - X; // SUB
|
||||
4'b0010: nX = ~X; // NOT
|
||||
4'b0011: nX = X & Y; // AND
|
||||
4'b0100: nX = X | Y; // OR
|
||||
4'b0101: nX = X ^ Y; // XOR
|
||||
4'b0110: nX = // CMP
|
||||
cmp_i ^ ((cmp_e && x_equals_y) || (cmp_l && y_lessthan_x));
|
||||
4'b0111: nX = Y; // Y
|
||||
4'b1000: nX = aluop_ext ? X >>> 1 : X >> 1; // SHR
|
||||
4'b1001: nX = operand[1] ? X << 2 : X << 1; // SHL
|
||||
4'b1010: nX = X + operand; // INC
|
||||
4'b1011: nX = X - operand; // DEC
|
||||
4'b1100: nX = // CMPU
|
||||
cmp_i ^ ((cmp_e && x_equals_y) || (cmp_l && yx_unsigned_less));
|
||||
// 4'b1101: nX = X[7:0] << ((3 - Y[1:0]) << 3); // BPLC
|
||||
4'b1101: nX = Y[1:0] == 0 ? { X[7:0], 24'b0 } :
|
||||
Y[1:0] == 1 ? { 8'b0, X[7:0], 16'b0 } :
|
||||
Y[1:0] == 2 ? { 16'b0, X[7:0], 8'b0 } :
|
||||
{ 24'b0, X[7:0]}; // BPLC
|
||||
4'b1110: nX = { X[23:16], X[15:8], X[7:0], X[31:24] }; // BROT
|
||||
4'b1111: nX = { 24'b0, Y[1:0] == 0 ? X[31:24] : Y[1:0] == 1 ? X[23:16] :
|
||||
Y[1:0] == 2 ? X[15: 8] : X[7:0] }; // BSEL
|
||||
// 4'b1110: nX = X * Y; // MUL
|
||||
// 4'b1111: nX = X / Y; // DIV
|
||||
default: nX = X;
|
||||
endcase
|
||||
end
|
||||
MEM:
|
||||
if (ins_loadi || ins_load || ins_loadrel)
|
||||
nX = data_in;
|
||||
endcase
|
||||
end
|
||||
|
||||
// estack movement
|
||||
wire [EVAL_STACK_INDEX_WIDTH-1:0] delta =
|
||||
((ins_load || ins_loadc || ins_loadreg || ins_loadrel)) ? 1
|
||||
: ((ins_aluop || ins_loadi || ins_storei || ins_xfer)) ?
|
||||
{ {(EVAL_STACK_INDEX_WIDTH-2){aluop_sd[1]}},aluop_sd} // sign extend
|
||||
: ((ins_store || ins_cbranch || ins_xfer || ins_storereg)) ? -1
|
||||
: 0;
|
||||
|
||||
|
||||
always @*
|
||||
begin
|
||||
if(reset)
|
||||
nESP <= 0;
|
||||
else
|
||||
if(seq_state == EXEC)
|
||||
begin
|
||||
nESP = ESP + delta;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
// when to write (old) X back to stack (new Y)
|
||||
// stack write is a reg so it is 1 in the next cycle i.e. MEM state
|
||||
stack_write <= (seq_state == EXEC &&
|
||||
(ins_load || ins_loadc || ins_loadrel || ins_loadreg
|
||||
|| ((ins_loadi || ins_storei || ins_aluop) && aluop_x2y)));
|
||||
end
|
||||
|
||||
// FP register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(seq_state == EXEC)
|
||||
begin
|
||||
if(ins_fpadj) FP <= FP + operand;
|
||||
else if(ins_storereg && ins_reg_fp) FP <= X;
|
||||
end
|
||||
end
|
||||
|
||||
// BP register
|
||||
always @(posedge clk) if(seq_state == EXEC && ins_storereg && ins_reg_bp) BP <= X;
|
||||
|
||||
// IV register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
IV <= 0;
|
||||
else if(seq_state == EXEC && ins_storereg && ins_reg_iv)
|
||||
IV <= X;
|
||||
end
|
||||
|
||||
// IR register
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(seq_state == MEM && irq_pending) IR <= nPC; // use nPC as interrupt return addr
|
||||
end
|
||||
|
||||
// process irq
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if(seq_state == MEM && irq_pending && !(ins_xfer & xfer_r2p)) // in FETCH state, clear irq_pending.
|
||||
irq_pending <= 0;
|
||||
else
|
||||
irq_pending <= irq_pending || irq; // else set irq_pending when irq is high
|
||||
end
|
||||
|
||||
// advance CPU state
|
||||
always @ (posedge clk)
|
||||
begin
|
||||
if(reset)
|
||||
{ PC, X, ESP, RP } <= { {WIDTH{1'b0}}, {WIDTH{1'b0}}, {WIDTH{1'b0}}, {WIDTH{1'b0}} };
|
||||
else if(seq_state == FETCH)
|
||||
{ PC, X, ESP, RP } <= { nPC, nX, nESP, nRP};
|
||||
end
|
||||
endmodule
|
||||
54
rtl/src/testbench.v
Normal file
54
rtl/src/testbench.v
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
`timescale 1ns/1ps
|
||||
`default_nettype none
|
||||
|
||||
module testbench();
|
||||
reg clk;
|
||||
reg rst_n;
|
||||
wire btn0;
|
||||
wire sw0;
|
||||
wire sw1;
|
||||
wire led0;
|
||||
wire led1;
|
||||
wire led2;
|
||||
wire led3;
|
||||
wire uart_txd_in;
|
||||
wire uart_rxd_out;
|
||||
|
||||
wire [15:0] ddr3_dq;
|
||||
wire [1:0] ddr3_dqs_n;
|
||||
wire [1:0] ddr3_dqs_p;
|
||||
wire [13:0] ddr3_addr;
|
||||
wire [2:0] ddr3_ba;
|
||||
wire ddr3_ras_n;
|
||||
wire ddr3_cas_n;
|
||||
wire ddr3_we_n;
|
||||
wire ddr3_reset_n;
|
||||
wire [0:0] ddr3_ck_p;
|
||||
wire [0:0] ddr3_ck_n;
|
||||
wire [0:0] ddr3_cke;
|
||||
wire [0:0] ddr3_cs_n;
|
||||
wire [1:0] ddr3_dm;
|
||||
wire [0:0] ddr3_odt;
|
||||
|
||||
integer t;
|
||||
|
||||
top top0(clk, rst_n, btn0, sw0,sw1, led0, led1, led2, led3, uart_txd_in, uart_rxd_out,
|
||||
ddr3_dq, ddr3_dqs_n, ddr3_dqs_p, ddr3_addr, ddr3_ba, ddr3_ras_n, ddr3_cas_n,
|
||||
ddr3_we_n, ddr3_reset_n, ddr3_ck_p, ddr3_ck_n, ddr3_cke, ddr3_cs_n, ddr3_dm, ddr3_odt);
|
||||
|
||||
initial begin
|
||||
clk = 1;
|
||||
t = 0;
|
||||
rst_n = 0;
|
||||
end
|
||||
|
||||
always #5.0 clk = ~clk;
|
||||
|
||||
always @(posedge clk) begin
|
||||
t <= t + 1;
|
||||
if (t == 2)
|
||||
rst_n = 1;
|
||||
if (t == 400)
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
288
rtl/src/top.v
Normal file
288
rtl/src/top.v
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
`timescale 1ns / 1ps
|
||||
// either define clock as clk (100MHz on Arty)
|
||||
// or as clk_1hz for debugging
|
||||
|
||||
`define clock cpuclk
|
||||
`define clkfreq 83333333
|
||||
//`define clock clk
|
||||
//`define clkfreq 100000000
|
||||
//`define clock clk_1hz
|
||||
`define ENABLE_VGAFB
|
||||
`define ENABLE_MICROSD
|
||||
|
||||
module top(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
input wire btn0,
|
||||
input wire sw0,
|
||||
input wire sw1,
|
||||
output wire led0,
|
||||
output wire led1,
|
||||
output wire led2,
|
||||
output wire led3,
|
||||
input wire uart_txd_in,
|
||||
output wire uart_rxd_out,
|
||||
|
||||
// DDR3 SDRAM
|
||||
inout wire [15:0] ddr3_dq,
|
||||
inout wire [1:0] ddr3_dqs_n,
|
||||
inout wire [1:0] ddr3_dqs_p,
|
||||
|
||||
output wire [13:0] ddr3_addr,
|
||||
output wire [2:0] ddr3_ba,
|
||||
output wire ddr3_ras_n,
|
||||
output wire ddr3_cas_n,
|
||||
output wire ddr3_we_n,
|
||||
output wire ddr3_reset_n,
|
||||
output wire [0:0] ddr3_ck_p,
|
||||
output wire [0:0] ddr3_ck_n,
|
||||
output wire [0:0] ddr3_cke,
|
||||
output wire [0:0] ddr3_cs_n,
|
||||
output wire [1:0] ddr3_dm,
|
||||
output wire [0:0] ddr3_odt
|
||||
|
||||
`ifdef ENABLE_VGAFB
|
||||
,
|
||||
output wire [3:0] VGA_R,
|
||||
output wire [3:0] VGA_G,
|
||||
output wire [3:0] VGA_B,
|
||||
|
||||
output wire VGA_HS_O,
|
||||
output wire VGA_VS_O
|
||||
`endif
|
||||
|
||||
`ifdef ENABLE_MICROSD
|
||||
,
|
||||
output wire sd_cs_n,
|
||||
output wire sd_mosi,
|
||||
input wire sd_miso,
|
||||
output wire sd_sck,
|
||||
input wire sd_cd
|
||||
`endif
|
||||
);
|
||||
|
||||
reg clk_1hz;
|
||||
reg [31:0] counter;
|
||||
|
||||
localparam ADDR_WIDTH = 32, WIDTH = 32,
|
||||
ROMADDR_WIDTH = 11, IOADDR_WIDTH = 11, IOADDR_SEL = 4;
|
||||
|
||||
wire [ADDR_WIDTH-1:0] mem_addr;
|
||||
wire [WIDTH-1:0] mem_read_data;
|
||||
wire [WIDTH-1:0] mem_write_data;
|
||||
(* KEEP *) wire mem_wait;
|
||||
|
||||
(* KEEP *) wire mem_read_enable;
|
||||
(* KEEP *) wire mem_write_enable;
|
||||
(* KEEP *) wire io_enable;
|
||||
wire [WIDTH-1:0] io_rd_data;
|
||||
wire [IOADDR_SEL-1:0] io_slot = mem_addr[IOADDR_WIDTH-1:IOADDR_WIDTH-IOADDR_SEL];
|
||||
|
||||
wire irq;
|
||||
|
||||
// assign led0 = mem_wait;
|
||||
|
||||
wire [WIDTH-1:0] debug_data1, debug_data2,
|
||||
debug_data3, debug_data4,
|
||||
debug_data5, debug_data6;
|
||||
|
||||
assign led0 = debug_data6[0];
|
||||
|
||||
wire cpuclk, cpuclk_locked;
|
||||
wire dram_refclk200;
|
||||
wire pixclk;
|
||||
cpu_clkgen cpuclk_0(~rst, clk, cpuclk, dram_refclk200, pixclk, cpuclk_locked);
|
||||
|
||||
// DRAM --------------------------------------------------------------------------
|
||||
wire [ADDR_WIDTH-1:0] dram_addr;
|
||||
wire [WIDTH-1:0] dram_read_data, dram_write_data;
|
||||
wire dram_read_enable, dram_write_enable, dram_wait;
|
||||
|
||||
dram_bridge dram_bridge0 (dram_addr,
|
||||
dram_read_data, dram_write_data, dram_read_enable, dram_write_enable, dram_wait,
|
||||
rst, cpuclk, dram_refclk200,
|
||||
ddr3_dq, ddr3_dqs_n, ddr3_dqs_p, ddr3_addr,
|
||||
ddr3_ba, ddr3_ras_n, ddr3_cas_n, ddr3_we_n,
|
||||
ddr3_reset_n, ddr3_ck_p, ddr3_ck_n, ddr3_cke,
|
||||
ddr3_cs_n, ddr3_dm, ddr3_odt);
|
||||
|
||||
mem #(.ADDR_WIDTH(ADDR_WIDTH), .DATA_WIDTH(WIDTH)) mem0(
|
||||
.clk(`clock), .rst_n(rst), .addr(mem_addr),
|
||||
.data_out(mem_read_data), .read_enable(mem_read_enable),
|
||||
.data_in(mem_write_data), .write_enable(mem_write_enable),
|
||||
.io_enable(io_enable),
|
||||
.io_rd_data(io_rd_data),
|
||||
.mem_wait(mem_wait),
|
||||
.dram_addr(dram_addr),
|
||||
.dram_read_data(dram_read_data),
|
||||
.dram_write_data(dram_write_data),
|
||||
.dram_read_enable(dram_read_enable),
|
||||
.dram_write_enable(dram_write_enable),
|
||||
.dram_wait(dram_wait)
|
||||
);
|
||||
|
||||
`ifdef ENABLE_VGAFB
|
||||
localparam FB_ADDR_WIDTH = 14;
|
||||
wire [FB_ADDR_WIDTH-1:0] fb_rd_addr;
|
||||
wire [FB_ADDR_WIDTH-1:0] fb_wr_addr;
|
||||
wire [WIDTH-1:0] fb_rd_data;
|
||||
wire [WIDTH-1:0] fb_wr_data;
|
||||
wire fb_rd_en, fb_wr_en;
|
||||
|
||||
wire fb_cs_en = io_enable && (io_slot == 2);
|
||||
|
||||
assign fb_rd_en = fb_cs_en && mem_read_enable;
|
||||
assign fb_wr_en = fb_cs_en && mem_write_enable;
|
||||
assign fb_wr_data = mem_write_data;
|
||||
|
||||
vgafb vgafb0(`clock, pixclk, rst,
|
||||
mem_addr[3:0], fb_rd_data, fb_wr_data,
|
||||
fb_rd_en, fb_wr_en,
|
||||
VGA_HS_O, VGA_VS_O, VGA_R, VGA_G, VGA_B);
|
||||
`endif
|
||||
|
||||
// SPI SD card controller -------------------------------------------------------------------
|
||||
`ifdef ENABLE_MICROSD
|
||||
wire [7:0] spi_tx_data;
|
||||
(*KEEP*) wire [7:0] spi_rx_data;
|
||||
wire spi_tx_ready; // ready to transmit new data
|
||||
wire spi_tx_empty; // tx fifo is empty
|
||||
wire spi_rx_avail; // a byte has been received
|
||||
wire spi_rx_ovr; // receiver overrun
|
||||
wire spi_tx_write; // write strobe
|
||||
wire spi_rx_read; // read strobe (clears rx_avail)
|
||||
|
||||
wire spi_card_detect; // true is card is present
|
||||
wire spi_card_changed; // card_detect signal has changed
|
||||
wire spi_card_busy; // card is busy (MISO/DO is 0)
|
||||
|
||||
wire spi_ctrl_write; // set the following flags
|
||||
wire spi_rx_filter_en; // set to wait for start bit (1-to-0) when receiving
|
||||
wire spi_txrx_en; // enable transmitter and receiver
|
||||
wire spi_sclk_f_en; // enable spi clock without transceiver
|
||||
wire spi_sclk_div_wr; // set clock divider from tx_data
|
||||
|
||||
wire spi_cs; // cs signal for spi controller
|
||||
wire [WIDTH-1:0] spi_rd_data;
|
||||
|
||||
assign spi_cs = io_enable && (io_slot == 1);
|
||||
|
||||
// spi read data: [ 0,...,0,cd,cc,cb,tr,te,ra,ro,d,d,d,d,d,d,d,d ]
|
||||
// cd = card detect, cc = card changed, cb = card busy,
|
||||
// tr = transmitter ready, te = tx fifo empty,
|
||||
// ra = received byte available, ro = receive overrun, d = received byte
|
||||
assign spi_rd_data =
|
||||
{ {WIDTH-15{1'b0}}, spi_card_detect, spi_card_changed, spi_card_busy,
|
||||
spi_tx_ready, spi_tx_empty,
|
||||
spi_rx_avail, spi_rx_ovr, spi_rx_data };
|
||||
|
||||
// spi write data: [ 0,...,0,CW,CF,Cx,Cc,Cd,DR,DW,d,d,d,d,d,d,d,d ]
|
||||
// CW = control write, CF = enable receive filter, Cx = enable transceiver,
|
||||
// Cc = force spi clock on, Cd = write clock divider,
|
||||
// DR = read acknowledge, DW = data write, d = byte to be sent
|
||||
assign spi_ctrl_write = spi_cs && mem_write_enable && mem_write_data[14];
|
||||
assign spi_rx_filter_en = mem_write_data[13];
|
||||
assign spi_txrx_en = mem_write_data[12];
|
||||
assign spi_sclk_f_en = mem_write_data[11];
|
||||
assign spi_sclk_div_wr = spi_cs && mem_write_enable && mem_write_data[10];
|
||||
assign spi_rx_read = mem_write_data[9];
|
||||
assign spi_tx_write = spi_cs && mem_write_enable && mem_write_data[8];
|
||||
assign spi_tx_data = mem_write_data[7:0];
|
||||
|
||||
sdspi sdspi0(.clk(`clock), .reset(~rst),
|
||||
.tx_data(spi_tx_data), .rx_data(spi_rx_data),
|
||||
.tx_ready(spi_tx_ready), .tx_empty(spi_tx_empty),
|
||||
.rx_avail(spi_rx_avail), .rx_ovr(spi_rx_ovr),
|
||||
.tx_write(spi_tx_write), .rx_read(spi_rx_read),
|
||||
.card_detect(spi_card_detect), .card_changed(spi_card_changed), .card_busy(spi_card_busy),
|
||||
// ctrl_write is used with rx_filter_en, txrx_en and spiclk_f_en
|
||||
.ctrl_write(spi_ctrl_write),
|
||||
.rx_filter_en(spi_rx_filter_en), .txrx_en(spi_txrx_en), .spiclk_f_en(spi_sclk_f_en),
|
||||
//
|
||||
.spiclk_div_wr(spi_sclk_div_wr),
|
||||
.sd_cs_n(sd_cs_n),
|
||||
.sd_mosi(sd_mosi), .sd_miso(sd_miso), .sd_sck(sd_sck), .sd_cd(sd_cd));
|
||||
`endif
|
||||
|
||||
// UART -----------------------------------------------------------------------
|
||||
|
||||
// uart write data: [ 0, 0, 0, 0, 0, T, C, 0, c, c, c, c, c, c, c, c ]
|
||||
// T = transmit enable, C = receiver clear, c = 8-bit-character
|
||||
// uart read data: [ 0, 0, 0, 0, 0, 0, A, B, c, c, c, c, c, c, c, c ]
|
||||
// A = char available, B = tx busy, c = 8-bit-character
|
||||
wire uart_cs = io_enable && (io_slot == 0);
|
||||
wire uart_tx_en = uart_cs && mem_write_enable && mem_write_data[10];
|
||||
wire uart_rx_clear = uart_cs && mem_write_enable && mem_write_data[9];
|
||||
wire uart_rx_avail;
|
||||
wire uart_rx_busy, uart_tx_busy;
|
||||
wire uart_err;
|
||||
wire [7:0] uart_rx_data;
|
||||
wire [7:0] uart_tx_data;
|
||||
wire [31:0] uart_baud = 32'd115200;
|
||||
wire [WIDTH-1:0] uart_rd_data;
|
||||
|
||||
assign uart_tx_data = mem_write_data[7:0];
|
||||
assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data };
|
||||
|
||||
reg timer_tick;
|
||||
reg[23:0] tick_count;
|
||||
wire [1:0] irq_in = { timer_tick, uart_rx_avail };
|
||||
wire [1:0] irqc_rd_data0;
|
||||
wire [WIDTH-1:0] irqc_rd_data = { tick_count, 6'b0, irqc_rd_data0 };
|
||||
wire irqc_seten = mem_write_data[7];
|
||||
wire irqc_cs = io_enable && (io_slot == 3);
|
||||
|
||||
assign io_rd_data = (io_slot == 0) ? uart_rd_data :
|
||||
`ifdef ENABLE_MICROSD
|
||||
(io_slot == 1) ? spi_rd_data :
|
||||
`endif
|
||||
`ifdef ENABLE_VGAFB
|
||||
(io_slot == 2) ? fb_rd_data :
|
||||
`endif
|
||||
(io_slot == 3) ? irqc_rd_data:
|
||||
|
||||
-1;
|
||||
|
||||
buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst,
|
||||
uart_baud,
|
||||
uart_txd_in, uart_rxd_out,
|
||||
uart_rx_clear, uart_tx_en,
|
||||
uart_rx_avail, uart_tx_busy,
|
||||
uart_tx_data, uart_rx_data);
|
||||
|
||||
// CPU -----------------------------------------------------------------
|
||||
stackcpu cpu0(.clk(`clock), .rst(rst), .irq(irq),
|
||||
.addr(mem_addr),
|
||||
.data_in(mem_read_data), .read_enable(mem_read_enable),
|
||||
.data_out(mem_write_data), .write_enable(mem_write_enable),
|
||||
.mem_wait(mem_wait),
|
||||
.led1(led1), .led2(led2), .led3(led3),
|
||||
.debug_out1(debug_data1),
|
||||
.debug_out2(debug_data2),
|
||||
.debug_out3(debug_data3),
|
||||
.debug_out4(debug_data4),
|
||||
.debug_out5(debug_data5),
|
||||
.debug_out6(debug_data6));
|
||||
|
||||
// Interrupt Controller
|
||||
irqctrl irqctrl0(`clock, irq_in, irqc_cs, mem_write_enable,
|
||||
irqc_seten, irqc_rd_data0,
|
||||
irq);
|
||||
|
||||
// count clock ticks
|
||||
// generate interrupt every 20nth of a second
|
||||
always @ (posedge `clock)
|
||||
begin
|
||||
counter <= counter + 1;
|
||||
if (counter >= (`clkfreq/20))
|
||||
begin
|
||||
counter <= 0;
|
||||
timer_tick <= 1;
|
||||
tick_count <= tick_count + 1'b1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
timer_tick <= 0;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
262
rtl/src/uart.v
Normal file
262
rtl/src/uart.v
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
Copyright (c) 2010-2020, James Bowman
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of swapforth nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
`default_nettype none
|
||||
//`define def_clkfreq 100000000
|
||||
`define def_clkfreq 83333333
|
||||
|
||||
module baudgen(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire restart,
|
||||
output wire ser_clk);
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
wire [38:0] aclkfreq = CLKFREQ;
|
||||
reg [38:0] d;
|
||||
wire [38:0] dInc = d[38] ? ({4'd0, baud}) : (({4'd0, baud}) - aclkfreq);
|
||||
wire [38:0] dN = restart ? 0 : (d + dInc);
|
||||
wire fastclk = ~d[38];
|
||||
assign ser_clk = fastclk;
|
||||
|
||||
always @(negedge resetq or posedge clk)
|
||||
begin
|
||||
if (!resetq) begin
|
||||
d <= 0;
|
||||
end else begin
|
||||
d <= dN;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
/*
|
||||
|
||||
-----+ +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----
|
||||
| | | | | | | | | | | |
|
||||
|start| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |stop1|stop2|
|
||||
| | | | | | | | | | | ? |
|
||||
+-----+-----+-----+-----+-----+-----+-----+-----+-----+ +
|
||||
|
||||
*/
|
||||
|
||||
module uart(
|
||||
input wire clk, // System clock
|
||||
input wire resetq,
|
||||
|
||||
// Outputs
|
||||
output wire uart_busy, // High means UART is transmitting
|
||||
output reg uart_tx, // UART transmit wire
|
||||
// Inputs
|
||||
input wire [31:0] baud,
|
||||
input wire uart_wr_i, // Raise to transmit byte
|
||||
input wire [7:0] uart_dat_i // 8-bit data
|
||||
);
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
reg [3:0] bitcount;
|
||||
reg [8:0] shifter;
|
||||
|
||||
assign uart_busy = |bitcount;
|
||||
wire sending = |bitcount;
|
||||
|
||||
wire ser_clk;
|
||||
|
||||
wire starting = uart_wr_i & ~uart_busy;
|
||||
baudgen #(.CLKFREQ(CLKFREQ)) _baudgen(
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.restart(1'b0),
|
||||
.ser_clk(ser_clk));
|
||||
|
||||
always @(negedge resetq or posedge clk)
|
||||
begin
|
||||
if (!resetq) begin
|
||||
uart_tx <= 1;
|
||||
bitcount <= 0;
|
||||
shifter <= 0;
|
||||
end else begin
|
||||
if (starting) begin
|
||||
shifter <= { uart_dat_i[7:0], 1'b0 };
|
||||
bitcount <= 1 + 8 + 1;
|
||||
end
|
||||
|
||||
if (sending & ser_clk) begin
|
||||
{ shifter, uart_tx } <= { 1'b1, shifter };
|
||||
bitcount <= bitcount - 4'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module rxuart(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire uart_rx, // UART recv wire
|
||||
input wire rd, // read strobe
|
||||
output wire valid, // has data
|
||||
output wire [7:0] data); // data
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
reg [4:0] bitcount;
|
||||
reg [7:0] shifter;
|
||||
|
||||
// On starting edge, wait 3 half-bits then sample, and sample every 2 bits thereafter
|
||||
|
||||
wire idle = &bitcount;
|
||||
wire sample;
|
||||
reg [2:0] hh = 3'b111;
|
||||
wire [2:0] hhN = {hh[1:0], uart_rx};
|
||||
wire startbit = idle & (hhN[2:1] == 2'b10);
|
||||
wire [7:0] shifterN = sample ? {hh[1], shifter[7:1]} : shifter;
|
||||
|
||||
wire ser_clk;
|
||||
baudgen #(.CLKFREQ(CLKFREQ)) _baudgen(
|
||||
.clk(clk),
|
||||
.baud({baud[30:0], 1'b0}),
|
||||
.resetq(resetq),
|
||||
.restart(startbit),
|
||||
.ser_clk(ser_clk));
|
||||
|
||||
assign valid = (bitcount == 18);
|
||||
reg [4:0] bitcountN;
|
||||
always @*
|
||||
if (startbit)
|
||||
bitcountN = 0;
|
||||
else if (!idle & !valid & ser_clk)
|
||||
bitcountN = bitcount + 5'd1;
|
||||
else if (valid & rd)
|
||||
bitcountN = 5'b11111;
|
||||
else
|
||||
bitcountN = bitcount;
|
||||
|
||||
// 3,5,7,9,11,13,15,17
|
||||
assign sample = (bitcount > 2) & bitcount[0] & !valid & ser_clk;
|
||||
assign data = shifter;
|
||||
|
||||
always @(negedge resetq or posedge clk)
|
||||
begin
|
||||
if (!resetq) begin
|
||||
hh <= 3'b111;
|
||||
bitcount <= 5'b11111;
|
||||
shifter <= 0;
|
||||
end else begin
|
||||
hh <= hhN;
|
||||
bitcount <= bitcountN;
|
||||
shifter <= shifterN;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
module fifo_rxuart(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire uart_rx, // UART recv wire
|
||||
input wire rd, // read strobe
|
||||
output wire valid, // has data
|
||||
output wire [7:0] data); // data
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
localparam ADDR_WIDTH = 6;
|
||||
localparam DATA_WIDTH = 8;
|
||||
|
||||
reg fifo_wr_en;
|
||||
(*KEEP*) wire fifo_rd_en, fifo_full, fifo_empty;
|
||||
wire [DATA_WIDTH-1:0] fifo_wr_data, fifo_rd_data;
|
||||
(*KEEP*) wire rx_avail;
|
||||
|
||||
assign valid = !fifo_empty;
|
||||
assign data = fifo_rd_data;
|
||||
assign fifo_rd_en = rd;
|
||||
|
||||
fifo #(.ADDR_WIDTH(ADDR_WIDTH)) rx_fifo(clk, ~resetq,
|
||||
fifo_wr_en, fifo_rd_en,
|
||||
fifo_wr_data, fifo_rd_data,
|
||||
fifo_full,
|
||||
fifo_empty
|
||||
);
|
||||
|
||||
rxuart #(.CLKFREQ(CLKFREQ)) _rx (
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.uart_rx(uart_rx),
|
||||
.rd(fifo_wr_en), // strobe read signal on fifo write
|
||||
.valid(rx_avail),
|
||||
.data(fifo_wr_data));
|
||||
|
||||
always @(posedge clk)
|
||||
begin
|
||||
if (!resetq)
|
||||
fifo_wr_en <= 0;
|
||||
else if(!fifo_wr_en && rx_avail) // ILA shows fifo_wr_en stays 0 ??
|
||||
fifo_wr_en <= 1; // pulse fifo_wr_en for one clock
|
||||
else // rx_avail goes zero one clock later
|
||||
fifo_wr_en <= 0;
|
||||
end
|
||||
endmodule
|
||||
|
||||
module buart(
|
||||
input wire clk,
|
||||
input wire resetq,
|
||||
input wire [31:0] baud,
|
||||
input wire rx, // recv wire
|
||||
output wire tx, // xmit wire
|
||||
input wire rd, // read strobe
|
||||
input wire wr, // write strobe
|
||||
output wire valid, // has recv data
|
||||
output wire busy, // is transmitting
|
||||
input wire [7:0] tx_data,
|
||||
output wire [7:0] rx_data // data
|
||||
);
|
||||
parameter CLKFREQ = `def_clkfreq;
|
||||
|
||||
fifo_rxuart #(.CLKFREQ(CLKFREQ)) _rx (
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.uart_rx(rx),
|
||||
.rd(rd),
|
||||
.valid(valid),
|
||||
.data(rx_data));
|
||||
uart #(.CLKFREQ(CLKFREQ)) _tx (
|
||||
.clk(clk),
|
||||
.resetq(resetq),
|
||||
.baud(baud),
|
||||
.uart_busy(busy),
|
||||
.uart_tx(tx),
|
||||
.uart_wr_i(wr),
|
||||
.uart_dat_i(tx_data));
|
||||
endmodule
|
||||
100
rtl/src/uart_tb.v
Normal file
100
rtl/src/uart_tb.v
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
// File Downloaded from http://www.nandland.com
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// This testbench will exercise both the UART Tx and Rx.
|
||||
// It sends out byte 0xAB over the transmitter
|
||||
// It then exercises the receive by receiving byte 0x3F
|
||||
`timescale 1ns/10ps
|
||||
|
||||
module uart_tb ();
|
||||
|
||||
// Testbench uses a 10 MHz clock
|
||||
// Want to interface to 115200 baud UART
|
||||
// 10000000 / 115200 = 87 Clocks Per Bit.
|
||||
parameter c_CLOCK_PERIOD_NS = 100;
|
||||
parameter c_CLKS_PER_BIT = 87;
|
||||
parameter c_BIT_PERIOD = 8600;
|
||||
|
||||
reg r_Clock = 0;
|
||||
reg r_Tx_DV = 0;
|
||||
wire w_Tx_Done;
|
||||
reg [7:0] r_Tx_Byte = 0;
|
||||
reg r_Rx_Serial = 1;
|
||||
wire [7:0] w_Rx_Byte;
|
||||
|
||||
|
||||
// Takes in input byte and serializes it
|
||||
task UART_WRITE_BYTE;
|
||||
input [7:0] i_Data;
|
||||
integer ii;
|
||||
begin
|
||||
|
||||
// Send Start Bit
|
||||
r_Rx_Serial <= 1'b0;
|
||||
#(c_BIT_PERIOD);
|
||||
#1000;
|
||||
|
||||
|
||||
// Send Data Byte
|
||||
for (ii=0; ii<8; ii=ii+1)
|
||||
begin
|
||||
r_Rx_Serial <= i_Data[ii];
|
||||
#(c_BIT_PERIOD);
|
||||
end
|
||||
|
||||
// Send Stop Bit
|
||||
r_Rx_Serial <= 1'b1;
|
||||
#(c_BIT_PERIOD);
|
||||
end
|
||||
endtask // UART_WRITE_BYTE
|
||||
|
||||
|
||||
uart_rx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_RX_INST
|
||||
(.i_Clock(r_Clock),
|
||||
.i_Rx_Serial(r_Rx_Serial),
|
||||
.o_Rx_DV(),
|
||||
.o_Rx_Byte(w_Rx_Byte)
|
||||
);
|
||||
|
||||
uart_tx #(.CLKS_PER_BIT(c_CLKS_PER_BIT)) UART_TX_INST
|
||||
(.i_Clock(r_Clock),
|
||||
.i_Tx_DV(r_Tx_DV),
|
||||
.i_Tx_Byte(r_Tx_Byte),
|
||||
.o_Tx_Active(),
|
||||
.o_Tx_Serial(),
|
||||
.o_Tx_Done(w_Tx_Done)
|
||||
);
|
||||
|
||||
|
||||
always
|
||||
#(c_CLOCK_PERIOD_NS/2) r_Clock <= !r_Clock;
|
||||
|
||||
|
||||
// Main Testing:
|
||||
initial
|
||||
begin
|
||||
|
||||
// Tell UART to send a command (exercise Tx)
|
||||
@(posedge r_Clock);
|
||||
@(posedge r_Clock);
|
||||
r_Tx_DV <= 1'b1;
|
||||
r_Tx_Byte <= 8'hAB;
|
||||
@(posedge r_Clock);
|
||||
r_Tx_DV <= 1'b0;
|
||||
@(posedge w_Tx_Done);
|
||||
|
||||
// Send a command to the UART (exercise Rx)
|
||||
@(posedge r_Clock);
|
||||
UART_WRITE_BYTE(8'h3F);
|
||||
@(posedge r_Clock);
|
||||
|
||||
// Check that the correct command was received
|
||||
if (w_Rx_Byte == 8'h3F)
|
||||
$display("Test Passed - Correct Byte Received");
|
||||
else
|
||||
$display("Test Failed - Incorrect Byte Received");
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
||||
292
rtl/src/vgafb.v
Normal file
292
rtl/src/vgafb.v
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
// Project F: Display Timings
|
||||
// (C)2019 Will Green, Open Source Hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
||||
//128K video memory is not enough for 640x480x4
|
||||
`define RES_640_400
|
||||
//`define RES_1024_768
|
||||
|
||||
module display_timings #(
|
||||
H_RES=640, // horizontal resolution (pixels)
|
||||
V_RES=480, // vertical resolution (lines)
|
||||
H_FP=16, // horizontal front porch
|
||||
H_SYNC=96, // horizontal sync
|
||||
H_BP=48, // horizontal back porch
|
||||
V_FP=10, // vertical front porch
|
||||
V_SYNC=2, // vertical sync
|
||||
V_BP=33, // vertical back porch
|
||||
H_POL=0, // horizontal sync polarity (0:neg, 1:pos)
|
||||
V_POL=0 // vertical sync polarity (0:neg, 1:pos)
|
||||
)
|
||||
(
|
||||
input wire i_pix_clk, // pixel clock
|
||||
input wire i_rst, // reset: restarts frame (active high)
|
||||
output wire o_hs, // horizontal sync
|
||||
output wire o_vs, // vertical sync
|
||||
output wire o_de, // display enable: high during active video
|
||||
output wire o_frame, // high for one tick at the start of each frame
|
||||
output wire o_scanline, // high for one tick at the start of each scanline
|
||||
output reg o_vblank, // high during vertical blank phase
|
||||
output reg signed [15:0] o_sx, // horizontal beam position (including blanking)
|
||||
output reg signed [15:0] o_sy // vertical beam position (including blanking)
|
||||
);
|
||||
|
||||
// Horizontal: sync, active, and pixels
|
||||
localparam signed H_STA = 0 - H_FP - H_SYNC - H_BP; // horizontal start
|
||||
localparam signed HS_STA = H_STA + H_FP; // sync start
|
||||
localparam signed HS_END = HS_STA + H_SYNC; // sync end
|
||||
localparam signed HA_STA = 0; // active start
|
||||
localparam signed HA_END = H_RES - 1; // active end
|
||||
|
||||
// Vertical: sync, active, and pixels
|
||||
localparam signed V_STA = 0 - V_FP - V_SYNC - V_BP; // vertical start
|
||||
localparam signed VS_STA = V_STA + V_FP; // sync start
|
||||
localparam signed VS_END = VS_STA + V_SYNC; // sync end
|
||||
localparam signed VA_STA = 0; // active start
|
||||
localparam signed VA_END = V_RES - 1; // active end
|
||||
|
||||
// generate sync signals with correct polarity
|
||||
assign o_hs = H_POL ? (o_sx > HS_STA && o_sx <= HS_END)
|
||||
: ~(o_sx > HS_STA && o_sx <= HS_END);
|
||||
assign o_vs = V_POL ? (o_sy > VS_STA && o_sy <= VS_END)
|
||||
: ~(o_sy > VS_STA && o_sy <= VS_END);
|
||||
|
||||
// display enable: high during active period
|
||||
assign o_de = (o_sx >= 0 && o_sy >= 0);
|
||||
|
||||
// o_frame: high for one tick at the start of each frame
|
||||
assign o_frame = (o_sy == V_STA && o_sx == H_STA);
|
||||
// o_scanline: high for one tick at the start of each visible scanline
|
||||
assign o_scanline = (o_sy >= VA_STA) && (o_sy <= VA_END) && (o_sx == H_STA);
|
||||
|
||||
always @(posedge i_pix_clk)
|
||||
begin
|
||||
if(o_frame) o_vblank <= 1;
|
||||
else if (o_de) o_vblank <= 0;
|
||||
end
|
||||
|
||||
always @ (posedge i_pix_clk)
|
||||
begin
|
||||
if (i_rst) // reset to start of frame
|
||||
begin
|
||||
o_sx <= H_STA;
|
||||
o_sy <= V_STA;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (o_sx == HA_END) // end of line
|
||||
begin
|
||||
o_sx <= H_STA;
|
||||
if (o_sy == VA_END) // end of frame
|
||||
o_sy <= V_STA;
|
||||
else
|
||||
o_sy <= o_sy + 16'sh1;
|
||||
end
|
||||
else
|
||||
o_sx <= o_sx + 16'sh1;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
||||
// Project F: Display Controller VGA Demo
|
||||
// (C)2020 Will Green, Open source hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
||||
// This demo requires the following Verilog modules:
|
||||
// * display_clocks
|
||||
// * display_timings
|
||||
// * test_card_simple or another test card
|
||||
|
||||
module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
|
||||
input wire cpu_clk, // cpu clock
|
||||
input wire CLK, // pixel clock
|
||||
input wire RST_BTN, // reset button
|
||||
input wire[3:0] reg_sel, // register select/address
|
||||
output wire [VMEM_DATA_WIDTH-1:0] rd_data,
|
||||
input wire [VMEM_DATA_WIDTH-1:0] wr_data,
|
||||
input wire rd_en,
|
||||
input wire wr_en,
|
||||
|
||||
output wire VGA_HS, // horizontal sync output
|
||||
output wire VGA_VS, // vertical sync output
|
||||
output wire [3:0] VGA_R, // 4-bit VGA red output
|
||||
output wire [3:0] VGA_G, // 4-bit VGA green output
|
||||
output wire [3:0] VGA_B // 4-bit VGA blue output
|
||||
);
|
||||
|
||||
localparam BITS_PER_PIXEL = 4; localparam MAX_SHIFT_COUNT = 7;
|
||||
localparam REG_RD_ADDR = 0; localparam REG_WR_ADDR = 1; localparam REG_VMEM = 2;
|
||||
localparam REG_PAL_SLOT = 3; localparam REG_PAL_DATA = 4;
|
||||
localparam REG_CTL = 5;
|
||||
|
||||
localparam COLOR_WIDTH = 12;
|
||||
localparam PALETTE_WIDTH = 4;
|
||||
|
||||
// Display Clocks
|
||||
wire pix_clk = CLK; // pixel clock
|
||||
wire clk_lock = 1; // clock locked?
|
||||
|
||||
wire vmem_rd_en;
|
||||
wire vmem_wr_en;
|
||||
wire [VMEM_DATA_WIDTH-1:0] vmem_rd_data;
|
||||
reg [VMEM_ADDR_WIDTH-1:0] cpu_rd_addr;
|
||||
reg [VMEM_ADDR_WIDTH-1:0] cpu_wr_addr;
|
||||
reg [VMEM_ADDR_WIDTH-1:0] pix_addr;
|
||||
wire [VMEM_DATA_WIDTH-1:0] pix_data;
|
||||
wire pix_rd;
|
||||
wire [VMEM_DATA_WIDTH-1:0] status;
|
||||
|
||||
assign vmem_rd_en = rd_en;
|
||||
assign vmem_wr_en = (reg_sel == REG_VMEM) && wr_en;
|
||||
assign rd_data = (reg_sel == REG_VMEM) ? vmem_rd_data :
|
||||
(reg_sel == REG_RD_ADDR) ? cpu_rd_addr :
|
||||
(reg_sel == REG_WR_ADDR) ? cpu_wr_addr :
|
||||
(reg_sel == REG_CTL) ? status :
|
||||
32'hFFFFFFFF;
|
||||
|
||||
wire [VMEM_ADDR_WIDTH-1:0] cpu_addr = vmem_wr_en ? cpu_wr_addr : cpu_rd_addr;
|
||||
|
||||
bram_tdp #(.DATA(VMEM_DATA_WIDTH),.ADDR(VMEM_ADDR_WIDTH)) vram0 (
|
||||
.a_rd(vmem_rd_en), .a_clk(cpu_clk),
|
||||
.a_wr(vmem_wr_en), .a_addr(cpu_addr), .a_din(wr_data),
|
||||
.a_dout(vmem_rd_data),
|
||||
.b_clk(pix_clk), .b_addr(pix_addr), .b_dout(pix_data),
|
||||
.b_rd(pix_rd)
|
||||
);
|
||||
|
||||
wire palette_wr_en = (reg_sel == REG_PAL_DATA) && wr_en;
|
||||
reg [PALETTE_WIDTH-1:0] palette_wr_slot;
|
||||
wire [COLOR_WIDTH-1:0] color_data;
|
||||
|
||||
palette palette0(.wr_clk(cpu_clk), .rd_clk(pix_clk), .wr_en(palette_wr_en),
|
||||
.wr_slot(palette_wr_slot), .wr_data(wr_data[COLOR_WIDTH-1:0]),
|
||||
.rd_slot(pixel[3:0]), .rd_data(color_data));
|
||||
|
||||
// Display Timings
|
||||
wire signed [15:0] sx; // horizontal screen position (signed)
|
||||
wire signed [15:0] sy; // vertical screen position (signed)
|
||||
wire h_sync; // horizontal sync
|
||||
wire v_sync; // vertical sync
|
||||
wire de; // display enable
|
||||
wire frame; // frame start
|
||||
wire scanline; // scanline start
|
||||
wire vblank; // vertical blank
|
||||
reg vblank_buf; // vertical blank in cpu clock domain
|
||||
|
||||
display_timings #( // 640x480 800x600 1280x720 1920x1080
|
||||
`ifdef RES_1024_768
|
||||
.H_RES(1024), // 640 800 1280 1920
|
||||
.V_RES(768), // 480 600 720 1080
|
||||
.H_FP(24), // 16 40 110 88
|
||||
.H_SYNC(136), // 96 128 40 44
|
||||
.H_BP(160), // 48 88 220 148
|
||||
.V_FP(3), // 10 1 5 4
|
||||
.V_SYNC(6), // 2 4 5 5
|
||||
.V_BP(29), // 33 23 20 36
|
||||
.H_POL(0), // 0 1 1 1
|
||||
.V_POL(0) // 0 1 1 1
|
||||
`endif
|
||||
`ifdef RES_640_400
|
||||
.H_RES(640),
|
||||
.V_RES(400),
|
||||
.H_FP(16),
|
||||
.H_SYNC(96),
|
||||
.H_BP(48),
|
||||
.V_FP(12),
|
||||
.V_SYNC(2),
|
||||
.V_BP(35),
|
||||
.H_POL(0),
|
||||
.V_POL(1)
|
||||
`endif
|
||||
)
|
||||
display_timings_inst (
|
||||
.i_pix_clk(CLK),
|
||||
.i_rst(!RST_BTN),
|
||||
.o_hs(h_sync),
|
||||
.o_vs(v_sync),
|
||||
.o_de(de),
|
||||
.o_frame(frame),
|
||||
.o_scanline(scanline),
|
||||
.o_vblank(vblank),
|
||||
.o_sx(sx),
|
||||
.o_sy(sy)
|
||||
);
|
||||
|
||||
wire [7:0] red;
|
||||
wire [7:0] green;
|
||||
wire [7:0] blue;
|
||||
|
||||
reg [VMEM_DATA_WIDTH-1:0] shifter;
|
||||
reg [4:0] shift_count;
|
||||
|
||||
// delayed frame signal for pix_rd
|
||||
reg frame_d;
|
||||
|
||||
assign pix_rd = frame_d || scanline || (shift_count == 2);
|
||||
|
||||
assign status = { 4'b0001, {(VMEM_DATA_WIDTH-5){1'b0}}, vblank_buf};
|
||||
|
||||
wire [BITS_PER_PIXEL-1:0] pixel = shifter[VMEM_DATA_WIDTH-1:VMEM_DATA_WIDTH-BITS_PER_PIXEL];
|
||||
|
||||
always @(posedge pix_clk) frame_d <= frame;
|
||||
|
||||
always @(posedge cpu_clk) vblank_buf <= vblank;
|
||||
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
if(wr_en)
|
||||
begin
|
||||
case(reg_sel)
|
||||
REG_RD_ADDR: cpu_rd_addr <= wr_data;
|
||||
REG_WR_ADDR: cpu_wr_addr <= wr_data;
|
||||
REG_VMEM: cpu_wr_addr <= cpu_wr_addr + 1; // auto-increment write addr on write
|
||||
REG_PAL_SLOT: palette_wr_slot <= wr_data[3:0];
|
||||
endcase
|
||||
end
|
||||
else
|
||||
if(rd_en && reg_sel == REG_VMEM) cpu_rd_addr <= cpu_rd_addr + 1; // auto-increment read addr on read
|
||||
end
|
||||
|
||||
always @(posedge pix_clk)
|
||||
begin
|
||||
if(scanline || shift_count == MAX_SHIFT_COUNT) // before start of a line
|
||||
begin // or at the end of a word, reset shifter with pixel data
|
||||
shift_count <= 0;
|
||||
shifter <= pix_data;
|
||||
end
|
||||
else if(de)
|
||||
begin
|
||||
shift_count <= shift_count + 1;
|
||||
shifter <= shifter << BITS_PER_PIXEL;
|
||||
end
|
||||
|
||||
if(frame) // at start of frame, reset pixel pointer
|
||||
pix_addr <= 0;
|
||||
else if(shift_count == 1) // after the first pixel, increment address
|
||||
pix_addr <= pix_addr + 1;
|
||||
end
|
||||
|
||||
// Hard-Coded RGBI palette
|
||||
// // Pixel = { red, green, blue, intensity }
|
||||
// assign red = pixel[3] ? pixel[0] ? 255 : 127: 0;
|
||||
// assign green = pixel[2] ? pixel[0] ? 255 : 127: 0;
|
||||
// assign blue = pixel[1] ? pixel[0] ? 255 : 127: 0;
|
||||
|
||||
// // VGA Output
|
||||
// // VGA Pmod is 12-bit so we take the upper nibble of each colour
|
||||
// assign VGA_HS = h_sync;
|
||||
// assign VGA_VS = v_sync;
|
||||
// assign VGA_R = de ? red[7:4] : 4'b0;
|
||||
// assign VGA_G = de ? green[7:4] : 4'b0;
|
||||
// assign VGA_B = de ? blue[7:4] : 4'b0;
|
||||
|
||||
// 12 bit RGB palette
|
||||
assign VGA_HS = h_sync;
|
||||
assign VGA_VS = v_sync;
|
||||
assign VGA_R = de ? color_data[11:8] : 4'b0;
|
||||
assign VGA_G = de ? color_data[7:4] : 4'b0;
|
||||
assign VGA_B = de ? color_data[3:0] : 4'b0;
|
||||
endmodule
|
||||
Loading…
Add table
Add a link
Reference in a new issue