From a2218de28150b29e00cd94dd778cae5ed274df70 Mon Sep 17 00:00:00 2001 From: Gyorgy Szombathelyi Date: Thu, 22 Jul 2021 14:41:18 +0200 Subject: [PATCH] Update MiST modules --- common/mist/README.md | 33 +++++++ common/mist/cofi.sv | 28 +++--- common/mist/mist.vhd | 16 ++-- common/mist/mist_core.qip | 7 ++ common/mist/mist_video.v | 113 ++++++++---------------- common/mist/scandoubler.v | 56 ++++++++++-- common/mist/sd_card.v | 167 ++++++++++++++++++++++-------------- common/mist/user_io.v | 175 ++++++++++++++++++++++++++++++++------ 8 files changed, 405 insertions(+), 190 deletions(-) create mode 100644 common/mist/README.md create mode 100644 common/mist/mist_core.qip diff --git a/common/mist/README.md b/common/mist/README.md new file mode 100644 index 00000000..588d54cd --- /dev/null +++ b/common/mist/README.md @@ -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]". diff --git a/common/mist/cofi.sv b/common/mist/cofi.sv index adc5c923..c519bd5d 100644 --- a/common/mist/cofi.sv +++ b/common/mist/cofi.sv @@ -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 diff --git a/common/mist/mist.vhd b/common/mist/mist.vhd index 0dfa2860..4a8199cd 100644 --- a/common/mist/mist.vhd +++ b/common/mist/mist.vhd @@ -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); diff --git a/common/mist/mist_core.qip b/common/mist/mist_core.qip new file mode 100644 index 00000000..1de1ee1c --- /dev/null +++ b/common/mist/mist_core.qip @@ -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] diff --git a/common/mist/mist_video.v b/common/mist/mist_video.v index 39774525..070ab50b 100644 --- a/common/mist/mist_video.v +++ b/common/mist/mist_video.v @@ -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 diff --git a/common/mist/scandoubler.v b/common/mist/scandoubler.v index 05e9286d..c9f0d684 100644 --- a/common/mist/scandoubler.v +++ b/common/mist/scandoubler.v @@ -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 diff --git a/common/mist/sd_card.v b/common/mist/sd_card.v index 12c956a9..43cca549 100644 --- a/common/mist/sd_card.v +++ b/common/mist/sd_card.v @@ -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 diff --git a/common/mist/user_io.v b/common/mist/user_io.v index 25cf9c4c..f0a5896c 100644 --- a/common/mist/user_io.v +++ b/common/mist/user_io.v @@ -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