1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-01-19 01:16:58 +00:00

Update MiST modules

This commit is contained in:
Gyorgy Szombathelyi 2021-07-22 14:41:18 +02:00
parent 6355a42aed
commit a2218de281
8 changed files with 405 additions and 190 deletions

33
common/mist/README.md Normal file
View File

@ -0,0 +1,33 @@
Common components for MiST board
================================
This repository contains common components, which should be used by almost all cores.
The modules:
- user_io.v - communicating with the IO controller.
- data_io.v - handling file uploads from the IO controller.
- mist_video.v - a video pipeline, which gives an optional scandoubler, OSD and rgb2ypbpr conversion.
- osd.v, scandoubler.v, rgb2ypbpr.sv, cofi.sv - these are used in mist_video, but can be used separately, too.
- sd_card.v - gives an SPI interface with SD-Card commands towards the IO-Controller, accessing .VHD and other mounted files.
- dac.vhd - a simple sigma-delta DAC for audio output.
- arcade_inputs.v - mostly for arcade-style games, gives access to the joysticks with MAME-style keyboard mapping.
- mist.vhd - VHDL component declarations for user_io and mist_video.
- mist_core.qip - collects the core components, which are needed in almost every case.
Usage hints
===========
All of these components should be clocked by a synchronous clock to the core. The data between the external SPI
interface and this internal clock are synchronized. However to make Quartus' job easier, you have to tell it to
don't try to optimize paths between the SPI and the system clock domain. Also you have to define the incoming
27 MHz and the SPI clocks. These lines in the .sdc file do that:
```
set sys_clk "your_system_clock"
create_clock -name {clk_27} -period 37.037 -waveform { 0.000 18.500 } [get_ports {CLOCK_27[0]}]
create_clock -name {SPI_SCK} -period 41.666 -waveform { 20.8 41.666 } [get_ports {SPI_SCK}]
set_clock_groups -asynchronous -group [get_clocks {SPI_SCK}] -group [get_clocks $sys_clk]
```
Replace "your_system_clock" with the name of the pll clock, like "pll|altpll_component|auto_generated|pll1|clk[0]".

View File

@ -1,5 +1,7 @@
// Composite-like horizontal blending by Kitrinx
// AMR - disable shift register recognition
(* altera_attribute = "-name AUTO_SHIFT_REGISTER_RECOGNITION OFF" *)
module cofi (
input clk,
input pix_ce,
@ -9,22 +11,24 @@ module cofi (
input vblank,
input hs,
input vs,
input [5:0] red,
input [5:0] green,
input [5:0] blue,
input [VIDEO_DEPTH-1:0] red,
input [VIDEO_DEPTH-1:0] green,
input [VIDEO_DEPTH-1:0] blue,
output reg hblank_out,
output reg vblank_out,
output reg hs_out,
output reg vs_out,
output reg [5:0] red_out,
output reg [5:0] green_out,
output reg [5:0] blue_out
output reg [VIDEO_DEPTH-1:0] red_out,
output reg [VIDEO_DEPTH-1:0] green_out,
output reg [VIDEO_DEPTH-1:0] blue_out
);
function bit [5:0] color_blend (
input [5:0] color_prev,
input [5:0] color_curr,
parameter VIDEO_DEPTH=8;
function bit [VIDEO_DEPTH-1:0] color_blend (
input [VIDEO_DEPTH-1:0] color_prev,
input [VIDEO_DEPTH-1:0] color_curr,
input blank_last
);
begin
@ -32,9 +36,9 @@ module cofi (
end
endfunction
reg [5:0] red_last;
reg [5:0] green_last;
reg [5:0] blue_last;
reg [VIDEO_DEPTH-1:0] red_last;
reg [VIDEO_DEPTH-1:0] green_last;
reg [VIDEO_DEPTH-1:0] blue_last;
wire ce = enable ? pix_ce : 1'b1;
always @(posedge clk) if (ce) begin

View File

@ -15,7 +15,9 @@ component user_io
generic(
STRLEN : integer := 0;
PS2DIV : integer := 100;
ROM_DIRECT_UPLOAD : boolean := false
ROM_DIRECT_UPLOAD : boolean := false;
SD_IMAGES: integer := 2;
PS2BIDIR : boolean := false
);
port (
clk_sys : in std_logic;
@ -39,14 +41,14 @@ port (
core_mod : out std_logic_vector(6 downto 0);
sd_lba : in std_logic_vector(31 downto 0) := (others => '0');
sd_rd : in std_logic_vector(1 downto 0) := (others => '0');
sd_wr : in std_logic_vector(1 downto 0) := (others => '0');
sd_rd : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0');
sd_wr : in std_logic_vector(SD_IMAGES-1 downto 0) := (others => '0');
sd_ack : out std_logic;
sd_ack_conf : out std_logic;
sd_conf : in std_logic := '0';
sd_sdhc : in std_logic := '1';
img_size : out std_logic_vector(31 downto 0);
img_mounted : out std_logic_vector(1 downto 0);
img_size : out std_logic_vector(63 downto 0);
img_mounted : out std_logic_vector(SD_IMAGES-1 downto 0);
sd_buff_addr : out std_logic_vector(8 downto 0);
sd_dout : out std_logic_vector(7 downto 0);
@ -56,6 +58,8 @@ port (
ps2_kbd_clk : out std_logic;
ps2_kbd_data : out std_logic;
ps2_kbd_clk_i : in std_logic := '1';
ps2_kbd_data_i : in std_logic := '1';
key_pressed : out std_logic;
key_extended : out std_logic;
key_code : out std_logic_vector(7 downto 0);
@ -63,6 +67,8 @@ port (
ps2_mouse_clk : out std_logic;
ps2_mouse_data : out std_logic;
ps2_mouse_clk_i : in std_logic := '1';
ps2_mouse_data_i : in std_logic := '1';
mouse_x : out signed(8 downto 0);
mouse_y : out signed(8 downto 0);
mouse_z : out signed(3 downto 0);

View File

@ -0,0 +1,7 @@
set_global_assignment -name VHDL_FILE [file join $::quartus(qip_path) mist.vhd]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) user_io.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) mist_video.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) scandoubler.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) osd.v]
set_global_assignment -name VERILOG_FILE [file join $::quartus(qip_path) rgb2ypbpr.v]
set_global_assignment -name SYSTEMVERILOG_FILE [file join $::quartus(qip_path) cofi.sv]

View File

@ -38,11 +38,11 @@ module mist_video
input VSync,
// MiST video output signals
output [5:0] VGA_R,
output [5:0] VGA_G,
output [5:0] VGA_B,
output VGA_VS,
output VGA_HS
output reg [5:0] VGA_R,
output reg [5:0] VGA_G,
output reg [5:0] VGA_B,
output reg VGA_VS,
output reg VGA_HS
);
parameter OSD_COLOR = 3'd4;
@ -59,59 +59,15 @@ wire [5:0] SD_B_O;
wire SD_HS_O;
wire SD_VS_O;
reg [5:0] R_full;
reg [5:0] G_full;
reg [5:0] B_full;
always @(*) begin
if (COLOR_DEPTH == 6) begin
R_full = R;
G_full = G;
B_full = B;
end else if (COLOR_DEPTH == 2) begin
R_full = {3{R}};
G_full = {3{G}};
B_full = {3{B}};
end else if (COLOR_DEPTH == 1) begin
R_full = {6{R}};
G_full = {6{G}};
B_full = {6{B}};
end else begin
R_full = { R, R[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] };
G_full = { G, G[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] };
B_full = { B, B[COLOR_DEPTH-1 -:(6-COLOR_DEPTH)] };
end
end
reg [1:0] i_div;
reg ce_x1, ce_x2;
always @(posedge clk_sys) begin
reg last_hs_in;
last_hs_in <= HSync;
if(last_hs_in & !HSync) begin
i_div <= 2'b00;
end else begin
i_div <= i_div + 2'd1;
end
end
always @(*) begin
if (!ce_divider) begin
ce_x1 = (i_div == 2'b01);
ce_x2 = i_div[0];
end else begin
ce_x1 = i_div[0];
ce_x2 = 1'b1;
end
end
wire pixel_ena;
scandoubler #(SD_HCNT_WIDTH, COLOR_DEPTH) scandoubler
(
.clk_sys ( clk_sys ),
.bypass ( scandoubler_disable ),
.ce_divider ( ce_divider ),
.scanlines ( scanlines ),
.ce_x1 ( ce_x1 ),
.ce_x2 ( ce_x2 ),
.pixel_ena ( pixel_ena ),
.hs_in ( HSync ),
.vs_in ( VSync ),
.r_in ( R ),
@ -132,15 +88,15 @@ osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE) osd
(
.clk_sys ( clk_sys ),
.rotate ( rotate ),
.ce ( scandoubler_disable ? ce_x1 : ce_x2 ),
.ce ( pixel_ena ),
.SPI_DI ( SPI_DI ),
.SPI_SCK ( SPI_SCK ),
.SPI_SS3 ( SPI_SS3 ),
.R_in ( scandoubler_disable ? R_full : SD_R_O ),
.G_in ( scandoubler_disable ? G_full : SD_G_O ),
.B_in ( scandoubler_disable ? B_full : SD_B_O ),
.HSync ( scandoubler_disable ? HSync : SD_HS_O ),
.VSync ( scandoubler_disable ? VSync : SD_VS_O ),
.R_in ( SD_R_O ),
.G_in ( SD_G_O ),
.B_in ( SD_B_O ),
.HSync ( SD_HS_O ),
.VSync ( SD_VS_O ),
.R_out ( osd_r_o ),
.G_out ( osd_g_o ),
.B_out ( osd_b_o )
@ -149,13 +105,13 @@ osd #(OSD_X_OFFSET, OSD_Y_OFFSET, OSD_COLOR, OSD_AUTO_CE) osd
wire [5:0] cofi_r, cofi_g, cofi_b;
wire cofi_hs, cofi_vs;
cofi cofi (
cofi #(6) cofi (
.clk ( clk_sys ),
.pix_ce ( scandoubler_disable ? ce_x1 : ce_x2 ),
.pix_ce ( pixel_ena ),
.enable ( blend ),
.hblank ( ~(scandoubler_disable ? HSync : SD_HS_O) ),
.hs ( scandoubler_disable ? HSync : SD_HS_O ),
.vs ( scandoubler_disable ? VSync : SD_VS_O ),
.hblank ( ~SD_HS_O ),
.hs ( SD_HS_O ),
.vs ( SD_VS_O ),
.red ( osd_r_o ),
.green ( osd_g_o ),
.blue ( osd_b_o ),
@ -166,7 +122,8 @@ cofi cofi (
.blue_out( cofi_b )
);
wire hs, vs;
wire hs, vs, cs;
wire [5:0] r,g,b;
RGBtoYPbPr #(6) rgb2ypbpr
(
@ -178,18 +135,22 @@ RGBtoYPbPr #(6) rgb2ypbpr
.blue_in ( cofi_b ),
.hs_in ( cofi_hs ),
.vs_in ( cofi_vs ),
.red_out ( VGA_R ),
.green_out ( VGA_G ),
.blue_out ( VGA_B ),
.cs_in ( SYNC_AND ? (cofi_hs & cofi_vs) : ~(cofi_hs ^ cofi_vs) ),
.red_out ( r ),
.green_out ( g ),
.blue_out ( b ),
.hs_out ( hs ),
.vs_out ( vs )
.vs_out ( vs ),
.cs_out ( cs )
);
wire cs = SYNC_AND ? (hs & vs) : ~(hs ^ vs);
// a minimig vga->scart cable expects a composite sync signal on the VGA_HS output.
// and VCC on VGA_VS (to switch into rgb mode)
assign VGA_HS = ((~no_csync & scandoubler_disable) || ypbpr)? cs : hs;
assign VGA_VS = ((~no_csync & scandoubler_disable) || ypbpr)? 1'b1 : vs;
always @(posedge clk_sys) begin
VGA_R <= r;
VGA_G <= g;
VGA_B <= b;
// a minimig vga->scart cable expects a composite sync signal on the VGA_HS output.
// and VCC on VGA_VS (to switch into rgb mode)
VGA_HS <= ((~no_csync & scandoubler_disable) || ypbpr)? cs : hs;
VGA_VS <= ((~no_csync & scandoubler_disable) || ypbpr)? 1'b1 : vs;
end
endmodule

View File

@ -22,11 +22,12 @@ module scandoubler
(
// system interface
input clk_sys,
input bypass,
input ce_divider,
output pixel_ena,
// scanlines (00-none 01-25% 10-50% 11-75%)
input [1:0] scanlines,
input ce_x1,
input ce_x2,
// shifter video interface
input hs_in,
@ -46,6 +47,32 @@ module scandoubler
parameter HCNT_WIDTH = 9;
parameter COLOR_DEPTH = 6;
// pixel clock divider
reg [1:0] i_div;
reg ce_x1, ce_x2;
always @(posedge clk_sys) begin
reg last_hs_in;
last_hs_in <= hs_in;
if(last_hs_in & !hs_in) begin
i_div <= 2'b00;
end else begin
i_div <= i_div + 2'd1;
end
end
always @(*) begin
if (!ce_divider) begin
ce_x1 = (i_div == 2'b01);
ce_x2 = i_div[0];
end else begin
ce_x1 = i_div[0];
ce_x2 = 1'b1;
end
end
assign pixel_ena = bypass ? ce_x1 : ce_x2;
// --------------------- create output signals -----------------
// latch everything once more to make it glitch free and apply scanline effect
reg scanline;
@ -74,9 +101,15 @@ always @(*) begin
end
always @(posedge clk_sys) begin
if(ce_x2) begin
if(bypass) begin
r_out <= r;
g_out <= g;
b_out <= b;
hs_out <= hs_sd;
vs_out <= vs_in;
vs_out <= vs_sd;
end else if(ce_x2) begin
hs_out <= hs_sd;
vs_out <= vs_sd;
// reset scanlines at every new screen
if(vs_out != vs_in) scanline <= 0;
@ -114,7 +147,7 @@ always @(posedge clk_sys) begin
end
// scan doubler output register
reg [COLOR_DEPTH*3-1:0] sd_out;
wire [COLOR_DEPTH*3-1:0] sd_out = bypass ? sd_bypass_out : sd_buffer_out;
// ==================================================================
// ======================== the line buffers ========================
@ -162,8 +195,9 @@ end
// ==================== output timing generation ====================
// ==================================================================
reg [HCNT_WIDTH-1:0] sd_hcnt;
reg hs_sd;
reg [COLOR_DEPTH*3-1:0] sd_buffer_out, sd_bypass_out;
reg [HCNT_WIDTH-1:0] sd_hcnt;
reg hs_sd, vs_sd;
// timing generation runs 32 MHz (twice the input signal analysis speed)
always @(posedge clk_sys) begin
@ -182,7 +216,13 @@ always @(posedge clk_sys) begin
if(sd_hcnt == hs_rise) hs_sd <= 1;
// read data from line sd_buffer
sd_out <= sd_buffer[{~line_toggle, sd_hcnt}];
sd_buffer_out <= sd_buffer[{~line_toggle, sd_hcnt}];
vs_sd <= vs_in;
end
if(bypass) begin
sd_bypass_out <= {r_in, g_in, b_in};
hs_sd <= hs_in;
vs_sd <= vs_in;
end
end

View File

@ -66,10 +66,11 @@ wire [7:0] READ_DATA_TOKEN = 8'hfe;
localparam NCR=4;
localparam RD_STATE_IDLE = 3'd0;
localparam RD_STATE_WAIT_IO = 3'd1;
localparam RD_STATE_SEND_TOKEN = 3'd2;
localparam RD_STATE_SEND_DATA = 3'd3;
localparam RD_STATE_DELAY = 3'd4;
localparam RD_STATE_WAIT_BUSY = 3'd1;
localparam RD_STATE_WAIT_IO = 3'd2;
localparam RD_STATE_SEND_TOKEN = 3'd3;
localparam RD_STATE_SEND_DATA = 3'd4;
localparam RD_STATE_DELAY = 3'd5;
reg [2:0] read_state = RD_STATE_IDLE;
localparam WR_STATE_IDLE = 3'd0;
@ -78,7 +79,8 @@ localparam WR_STATE_RECV_DATA = 3'd2;
localparam WR_STATE_RECV_CRC0 = 3'd3;
localparam WR_STATE_RECV_CRC1 = 3'd4;
localparam WR_STATE_SEND_DRESP = 3'd5;
localparam WR_STATE_BUSY = 3'd6;
localparam WR_STATE_WRITE = 3'd6;
localparam WR_STATE_BUSY = 3'd7;
reg [2:0] write_state = WR_STATE_IDLE;
reg card_is_reset = 1'b0; // flag that card has received a reset command
@ -89,6 +91,7 @@ reg [7:0] cmd = 8'h00;
reg [2:0] bit_cnt = 3'd0; // counts bits 0-7 0-7 ...
reg [3:0] byte_cnt= 4'd15; // counts bytes
reg [4:0] delay_cnt;
reg wr_first;
reg [39:0] args;
@ -110,7 +113,7 @@ sd_card_dpram #(8, 10) buffer_dpram
.clock_a (clk_sys),
.address_a ({sd_buff_sel, sd_buff_addr}),
.data_a (sd_buff_dout),
.wren_a (sd_buff_wr & sd_ack),
.wren_a (sd_buff_wr & sd_ack & read_state != RD_STATE_IDLE),
.q_a (sd_buff_din),
.clock_b (clk_sys),
@ -152,7 +155,7 @@ always @(posedge clk_sys) begin
// update card size in case of a virtual SD image
if (sd_sdhc)
// CSD V2.0 size = (c_size + 1) * 512K
csdcid[69:48] <= {9'd0, img_size[31:19] };
csdcid[69:48] <= {9'd0, img_size[31:19] } - 1'd1;
else begin
// CSD V1.0 no. of blocks = c_size ** (c_size_mult + 2)
csdcid[49:47] <= 3'd7; //c_size_mult
@ -196,12 +199,9 @@ always@(posedge clk_sys) begin
// CMD17: READ_SINGLE_BLOCK
// CMD18: READ_MULTIPLE_BLOCK
if((cmd == 8'h51 || cmd == 8'h52) && !terminate_cmd) begin
sd_lba <= sd_sdhc?args[39:8]:{9'd0, args[39:17]};
read_state <= RD_STATE_WAIT_IO; // start waiting for data from io controller
sd_rd <= 1; // trigger request to io controller
sd_busy <= 1;
end
if((cmd == 8'h51 || cmd == 8'h52) && !terminate_cmd)
read_state <= RD_STATE_WAIT_BUSY; // start waiting for data from io controller
end
end
else if((reply_len > 0) && (byte_cnt == 5+NCR+1))
@ -219,10 +219,19 @@ always@(posedge clk_sys) begin
RD_STATE_IDLE: ;
// don't do anything
// wait until the IO controller is free and issue a read
RD_STATE_WAIT_BUSY:
if (~sd_busy) begin
sd_buff_sel <= 0;
sd_lba <= sd_sdhc?args[39:8]:{9'd0, args[39:17]};
sd_rd <= 1; // trigger request to io controller
sd_busy <= 1;
read_state <= RD_STATE_WAIT_IO;
end
// waiting for io controller to return data
RD_STATE_WAIT_IO: begin
buffer_ptr <= 0;
sd_buff_sel <= 0;
if(~sd_busy) begin
if (terminate_cmd) begin
cmd <= 0;
@ -299,7 +308,7 @@ always@(posedge clk_sys) begin
sd_sdo <= WRITE_DATA_RESPONSE[~bit_cnt];
// busy after write until the io controller sends ack
if(write_state == WR_STATE_BUSY)
if(write_state == WR_STATE_WRITE || write_state == WR_STATE_BUSY)
sd_sdo <= 1'b0;
end
@ -310,6 +319,7 @@ always@(posedge clk_sys) begin
terminate_cmd <= 0;
cmd <= 0;
read_state <= RD_STATE_IDLE;
reply_len <= 0;
end else if (~old_sd_sck & sd_sck) begin
bit_cnt <= bit_cnt + 3'd1;
@ -325,7 +335,7 @@ always@(posedge clk_sys) begin
// byte_cnt > 6 -> complete command received
// first byte of valid command is 01xxxxxx
// don't accept new commands (except STOP TRANSMISSION) once a write or read command has been accepted
if((byte_cnt > 5) &&
if((byte_cnt > (reply_len == 0 ? 5 : (5+NCR+reply_len))) &&
(write_state == WR_STATE_IDLE) &&
(read_state == RD_STATE_IDLE || (read_state != RD_STATE_IDLE && { sbuf, sd_sdi} == 8'h4c)) &&
sbuf[6:5] == 2'b01)
@ -406,9 +416,11 @@ always@(posedge clk_sys) begin
8'h52: reply <= 8'h00; // ok
// CMD24: WRITE_BLOCK
8'h58: begin
// CMD25: WRITE_MULTIPLE_BLOCKS
8'h58, 8'h59: begin
reply <= 8'h00; // ok
sd_lba <= sd_sdhc?args[39:8]:{9'd0, args[39:17]};
buffer_ptr <= 0;
wr_first <= 1;
write_state <= WR_STATE_EXP_DTOKEN; // expect data token
end
@ -433,58 +445,83 @@ always@(posedge clk_sys) begin
reply3 <= OCR[7:0];
reply_len <= 4'd4;
end
endcase
end
end
// ---------- handle write -----------
case(write_state)
// don't do anything in idle state
WR_STATE_IDLE: ;
// waiting for data token
WR_STATE_EXP_DTOKEN:
if({ sbuf, sd_sdi} == 8'hfe ) begin
write_state <= WR_STATE_RECV_DATA;
buffer_ptr <= 0;
sd_buff_sel <= 0;
end
// transfer 512 bytes
WR_STATE_RECV_DATA: begin
// push one byte into local buffer
buffer_write_strobe <= 1'b1;
buffer_din <= { sbuf, sd_sdi };
// all bytes written?
if(&buffer_ptr[8:0])
write_state <= WR_STATE_RECV_CRC0;
end
// transfer 1st crc byte
WR_STATE_RECV_CRC0:
write_state <= WR_STATE_RECV_CRC1;
// transfer 2nd crc byte
WR_STATE_RECV_CRC1:
write_state <= WR_STATE_SEND_DRESP;
// send data response
WR_STATE_SEND_DRESP: begin
write_state <= WR_STATE_BUSY;
sd_wr <= 1; // trigger write request to io ontroller
sd_busy <= 1;
end
// wait for io controller to accept data
WR_STATE_BUSY:
if(~sd_busy)
write_state <= WR_STATE_IDLE;
default: ;
endcase
end
end
// ---------- handle write -----------
case(write_state)
// don't do anything in idle state
WR_STATE_IDLE: ;
// waiting for data token
WR_STATE_EXP_DTOKEN:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) begin
if({ sbuf, sd_sdi} == 8'hfd && cmd == 8'h59)
// stop multiple write (and wait until the last write finishes)
write_state <= WR_STATE_BUSY;
else
if({ sbuf, sd_sdi} == ((cmd == 8'h59) ? 8'hfc : 8'hfe))
write_state <= WR_STATE_RECV_DATA;
end
// transfer 512 bytes
WR_STATE_RECV_DATA:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) begin
// push one byte into local buffer
buffer_write_strobe <= 1'b1;
buffer_din <= { sbuf, sd_sdi };
// all bytes written?
if(&buffer_ptr[8:0])
write_state <= WR_STATE_RECV_CRC0;
end
// transfer 1st crc byte
WR_STATE_RECV_CRC0:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_RECV_CRC1;
// transfer 2nd crc byte
WR_STATE_RECV_CRC1:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_SEND_DRESP;
// send data response
WR_STATE_SEND_DRESP:
if (sd_cs) write_state <= WR_STATE_IDLE;
else if (~old_sd_sck && sd_sck && bit_cnt == 7) write_state <= WR_STATE_WRITE;
WR_STATE_WRITE:
if (~sd_busy) begin
if (wr_first) begin
sd_buff_sel <= 0;
sd_lba <= sd_sdhc?args[39:8]:{9'd0, args[39:17]};
wr_first <= 0;
end else begin
sd_buff_sel <= !sd_buff_sel;
sd_lba <= sd_lba + 1'd1;
end
sd_wr <= 1; // trigger write request to io controller
sd_busy <= 1;
if (sd_cs || cmd == 8'h58)
write_state <= WR_STATE_BUSY;
else
write_state <= WR_STATE_EXP_DTOKEN; // multi-sector writes
end
WR_STATE_BUSY:
if (~sd_busy) write_state <= WR_STATE_IDLE;
default: ;
endcase
end
endmodule

View File

@ -22,7 +22,7 @@
// parameter STRLEN and the actual length of conf_str have to match
module user_io #(parameter STRLEN=0, parameter PS2DIV=100, parameter ROM_DIRECT_UPLOAD=0) (
module user_io (
input [(8*STRLEN)-1:0] conf_str,
output [9:0] conf_addr, // RAM address for config string, if STRLEN=0
input [7:0] conf_chr,
@ -55,8 +55,8 @@ module user_io #(parameter STRLEN=0, parameter PS2DIV=100, parameter ROM_DIRECT_
// connection to sd card emulation
input [31:0] sd_lba,
input [1:0] sd_rd,
input [1:0] sd_wr,
input [SD_IMAGES-1:0] sd_rd,
input [SD_IMAGES-1:0] sd_wr,
output reg sd_ack,
output reg sd_ack_conf,
input sd_conf,
@ -67,14 +67,18 @@ module user_io #(parameter STRLEN=0, parameter PS2DIV=100, parameter ROM_DIRECT_
output reg sd_din_strobe,
output reg [8:0] sd_buff_addr,
output reg [1:0] img_mounted, // rising edge if a new image is mounted
output reg [31:0] img_size, // size of image in bytes
output reg [SD_IMAGES-1:0] img_mounted, // rising edge if a new image is mounted
output reg [63:0] img_size, // size of image in bytes
// ps2 keyboard/mouse emulation
output ps2_kbd_clk,
output reg ps2_kbd_data,
input ps2_kbd_clk_i,
input ps2_kbd_data_i,
output ps2_mouse_clk,
output reg ps2_mouse_data,
input ps2_mouse_clk_i,
input ps2_mouse_data_i,
// keyboard data
output reg key_pressed, // 1-make (pressed), 0-break (released)
@ -95,6 +99,14 @@ module user_io #(parameter STRLEN=0, parameter PS2DIV=100, parameter ROM_DIRECT_
input serial_strobe
);
parameter STRLEN=0; // config string length
parameter PS2DIV=100; // master clock divider for psk2_kbd/mouse clk
parameter ROM_DIRECT_UPLOAD=0; // direct upload used for file uploads from the ARM
parameter SD_IMAGES=2; // number of block-access images (max. 4 supported in current firmware)
parameter PS2BIDIR=0; // bi-directional PS2 interface
localparam W = $clog2(SD_IMAGES);
reg [6:0] sbuf;
reg [7:0] cmd;
reg [2:0] bit_cnt; // counts bits 0-7 0-7 ...
@ -114,7 +126,13 @@ assign conf_addr = byte_cnt;
// bit 4 indicates ROM direct upload capability
wire [7:0] core_type = ROM_DIRECT_UPLOAD ? 8'hb4 : 8'ha4;
wire drive_sel = sd_rd[1] | sd_wr[1];
reg [W:0] drive_sel;
always begin
integer i;
drive_sel = 0;
for(i = 0; i < SD_IMAGES; i = i + 1) if(sd_rd[i] | sd_wr[i]) drive_sel = i[W:0];
end
// command byte read by the io controller
wire [7:0] sd_cmd = { 4'h6, sd_conf, sd_sdhc, sd_wr[drive_sel], sd_rd[drive_sel] };
@ -125,7 +143,7 @@ wire spi_sck = SPI_CLK;
localparam PS2_FIFO_BITS = 3;
reg ps2_clk;
always @(negedge clk_sys) begin
always @(posedge clk_sys) begin
integer cnt;
cnt <= cnt + 1'd1;
if(cnt == PS2DIV) begin
@ -144,14 +162,24 @@ reg [3:0] ps2_kbd_tx_state;
reg [7:0] ps2_kbd_tx_byte;
reg ps2_kbd_parity;
assign ps2_kbd_clk = ps2_clk || (ps2_kbd_tx_state == 0);
// ps2 receiver state machine
reg [3:0] ps2_kbd_rx_state = 0;
reg [1:0] ps2_kbd_rx_start = 0;
reg [7:0] ps2_kbd_rx_byte = 0;
reg ps2_kbd_rx_strobe = 0;
// ps2 transmitter
assign ps2_kbd_clk = ps2_clk || (ps2_kbd_tx_state == 0 && ps2_kbd_rx_state == 0);
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
reg ps2_kbd_r_inc;
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_kbd
reg ps2_clkD;
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_kbd_r_inc;
// send data
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
ps2_kbd_r_inc <= 1'b0;
@ -161,8 +189,9 @@ always@(posedge clk_sys) begin : ps2_kbd
// transmitter is idle?
if(ps2_kbd_tx_state == 0) begin
ps2_kbd_data <= 1;
// data in fifo present?
if(ps2_kbd_wptr != ps2_kbd_rptr) begin
if(ps2_kbd_wptr != ps2_kbd_rptr && (ps2_kbd_clk_i | !PS2BIDIR)) begin
// load tx register from fifo
ps2_kbd_tx_byte <= ps2_kbd_fifo[ps2_kbd_rptr];
ps2_kbd_r_inc <= 1'b1;
@ -201,6 +230,43 @@ always@(posedge clk_sys) begin : ps2_kbd
ps2_kbd_tx_state <= 4'd0;
end
end
if (PS2BIDIR) begin
ps2_clk_iD <= ps2_kbd_clk_i;
ps2_dat_iD <= ps2_kbd_data_i;
// receive command
case (ps2_kbd_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_kbd_clk_i) ps2_kbd_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_kbd_data_i) ps2_kbd_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_kbd_clk_i) ps2_kbd_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_kbd_rx_state <= 4'd1;
ps2_kbd_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_kbd_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_kbd_rx_state <= ps2_kbd_rx_state + 1'd1;
if (ps2_kbd_rx_state == 9) ;// parity
else if (ps2_kbd_rx_state == 10) begin
ps2_kbd_data <= 0; // ack the received byte
end else if (ps2_kbd_rx_state == 11) begin
ps2_kbd_rx_state <= 0;
ps2_kbd_rx_strobe <= ~ps2_kbd_rx_strobe;
end else begin
ps2_kbd_rx_byte <= {ps2_kbd_data_i, ps2_kbd_rx_byte[7:1]};
end
end
end
end
// mouse
@ -213,13 +279,21 @@ reg [3:0] ps2_mouse_tx_state;
reg [7:0] ps2_mouse_tx_byte;
reg ps2_mouse_parity;
assign ps2_mouse_clk = ps2_clk || (ps2_mouse_tx_state == 0);
// ps2 receiver state machine
reg [3:0] ps2_mouse_rx_state = 0;
reg [1:0] ps2_mouse_rx_start = 0;
reg [7:0] ps2_mouse_rx_byte = 0;
reg ps2_mouse_rx_strobe = 0;
// ps2 transmitter
assign ps2_mouse_clk = ps2_clk || (ps2_mouse_tx_state == 0 && ps2_mouse_rx_state == 0);
// ps2 transmitter/receiver
// Takes a byte from the FIFO and sends it in a ps2 compliant serial format.
reg ps2_mouse_r_inc;
// Sends a command to the IO controller if bidirectional mode is enabled.
always@(posedge clk_sys) begin : ps2_mouse
reg ps2_clkD;
reg ps2_clk_iD, ps2_dat_iD;
reg ps2_mouse_r_inc;
ps2_clkD <= ps2_clk;
if (~ps2_clkD & ps2_clk) begin
@ -230,8 +304,9 @@ always@(posedge clk_sys) begin : ps2_mouse
// transmitter is idle?
if(ps2_mouse_tx_state == 0) begin
ps2_mouse_data <= 1;
// data in fifo present?
if(ps2_mouse_wptr != ps2_mouse_rptr) begin
if(ps2_mouse_wptr != ps2_mouse_rptr && (ps2_mouse_clk_i | !PS2BIDIR)) begin
// load tx register from fifo
ps2_mouse_tx_byte <= ps2_mouse_fifo[ps2_mouse_rptr];
ps2_mouse_r_inc <= 1'b1;
@ -270,6 +345,44 @@ always@(posedge clk_sys) begin : ps2_mouse
ps2_mouse_tx_state <= 4'd0;
end
end
if (PS2BIDIR) begin
ps2_clk_iD <= ps2_mouse_clk_i;
ps2_dat_iD <= ps2_mouse_data_i;
// receive command
case (ps2_mouse_rx_start)
2'd0:
// first: host pulls down the clock line
if (ps2_clk_iD & ~ps2_mouse_clk_i) ps2_mouse_rx_start <= 1;
2'd1:
// second: host pulls down the data line, while releasing the clock
if (ps2_dat_iD && !ps2_mouse_data_i) ps2_mouse_rx_start <= 2'd2;
// if it releases the clock without pulling down the data line: goto 0
else if (ps2_mouse_clk_i) ps2_mouse_rx_start <= 0;
2'd2:
if (ps2_clkD && ~ps2_clk) begin
ps2_mouse_rx_state <= 4'd1;
ps2_mouse_rx_start <= 0;
end
default: ;
endcase
// host data is valid after the rising edge of the clock
if(ps2_mouse_rx_state != 0 && ~ps2_clkD && ps2_clk) begin
ps2_mouse_rx_state <= ps2_mouse_rx_state + 1'd1;
if (ps2_mouse_rx_state == 9) ;// parity
else if (ps2_mouse_rx_state == 10) begin
ps2_mouse_data <= 0; // ack the received byte
end else if (ps2_mouse_rx_state == 11) begin
ps2_mouse_rx_state <= 0;
ps2_mouse_rx_strobe <= ~ps2_mouse_rx_strobe;
end else begin
ps2_mouse_rx_byte <= {ps2_mouse_data_i, ps2_mouse_rx_byte[7:1]};
end
end
end
end
// fifo to receive serial data from core to be forwarded to io controller
@ -334,7 +447,9 @@ end
always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter
reg [31:0] sd_lba_r;
reg [7:0] drive_sel_r;
reg [W:0] drive_sel_r;
reg ps2_kbd_rx_strobeD;
reg ps2_mouse_rx_strobeD;
if(SPI_SS_IO == 1) begin
spi_byte_out <= core_type;
@ -345,6 +460,20 @@ always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter
spi_byte_out <= 0;
case({(!byte_cnt) ? {sbuf, SPI_MOSI} : cmd})
// PS2 keyboard command
8'h0e: if (byte_cnt == 0) begin
ps2_kbd_rx_strobeD <= ps2_kbd_rx_strobe;
//echo the command code if there's a byte to send, indicating the core supports the command
spi_byte_out <= (ps2_kbd_rx_strobe ^ ps2_kbd_rx_strobeD) ? 8'h0e : 8'h00;
end else spi_byte_out <= ps2_kbd_rx_byte;
// PS2 mouse command
8'h0f: if (byte_cnt == 0) begin
ps2_mouse_rx_strobeD <= ps2_mouse_rx_strobe;
//echo the command code if there's a byte to send, indicating the core supports the command
spi_byte_out <= (ps2_mouse_rx_strobe ^ ps2_mouse_rx_strobeD) ? 8'h0f : 8'h00;
end else spi_byte_out <= ps2_mouse_rx_byte;
// reading config string
8'h14: if (STRLEN == 0) spi_byte_out <= conf_chr; else
if(byte_cnt < STRLEN) spi_byte_out <= conf_str[(STRLEN - byte_cnt - 1)<<3 +:8];
@ -353,7 +482,7 @@ always@(posedge spi_sck or posedge SPI_SS_IO) begin : spi_transmitter
8'h16: if(byte_cnt == 0) begin
spi_byte_out <= sd_cmd;
sd_lba_r <= sd_lba;
drive_sel_r <= {7'b0, drive_sel};
drive_sel_r <= drive_sel;
end
else if(byte_cnt == 1) spi_byte_out <= drive_sel_r;
else if(byte_cnt < 6) spi_byte_out <= sd_lba_r[(5-byte_cnt)<<3 +:8];
@ -521,7 +650,7 @@ always @(posedge clk_sd) begin : sd_block
reg spi_transfer_end;
reg spi_receiver_strobeD;
reg spi_transfer_endD;
reg [1:0] sd_wrD;
reg [SD_IMAGES-1:0] sd_wrD;
reg [7:0] acmd;
reg [7:0] abyte_cnt; // counts bytes
@ -539,7 +668,7 @@ always @(posedge clk_sd) begin : sd_block
sd_din_strobe<= 0;
sd_wrD <= sd_wr;
// fetch the first byte immediately after the write command seen
if ((~sd_wrD[0] & sd_wr[0]) || (~sd_wrD[1] & sd_wrD[1])) begin
if (|(~sd_wrD & sd_wr)) begin
sd_buff_addr <= 0;
sd_din_strobe <= 1;
end
@ -550,8 +679,6 @@ always @(posedge clk_sd) begin : sd_block
abyte_cnt <= 8'd0;
sd_ack <= 1'b0;
sd_ack_conf <= 1'b0;
sd_dout_strobe <= 1'b0;
sd_din_strobe <= 1'b0;
sd_buff_addr <= 0;
end else if (spi_receiver_strobeD ^ spi_receiver_strobe) begin
@ -591,10 +718,10 @@ always @(posedge clk_sd) begin : sd_block
end
end
8'h1c: img_mounted[spi_byte_in[0]] <= 1;
8'h1c: img_mounted[spi_byte_in[W:0]] <= 1;
// send image info
8'h1d: if(abyte_cnt<5) img_size[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
8'h1d: if(abyte_cnt<9) img_size[(abyte_cnt-1)<<3 +:8] <= spi_byte_in;
endcase
end
end