From b277089158eeedb33e93604f1fffb079951960fd Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 4 Apr 2021 14:14:14 +0200 Subject: [PATCH] Update Mist Modules --- common/mist/README.md | 33 --- common/mist/arcade_inputs.v | 6 +- common/mist/sd_card.v | 525 ++++++++++++++++++++++++++++++++++++ 3 files changed, 528 insertions(+), 36 deletions(-) delete mode 100644 common/mist/README.md create mode 100644 common/mist/sd_card.v diff --git a/common/mist/README.md b/common/mist/README.md deleted file mode 100644 index 588d54cd..00000000 --- a/common/mist/README.md +++ /dev/null @@ -1,33 +0,0 @@ -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/arcade_inputs.v b/common/mist/arcade_inputs.v index 8d0fdebf..9c28c93f 100644 --- a/common/mist/arcade_inputs.v +++ b/common/mist/arcade_inputs.v @@ -112,10 +112,10 @@ always @(posedge clk) begin 'h06: btn_two_players <= key_pressed; // F2 'h04: btn_three_players <= key_pressed; // F3 'h0C: btn_four_players <= key_pressed; // F4 - 'h14: btn_fireA <= key_pressed; // ctrl - 'h11: btn_fireB <= key_pressed; // alt - 'h29: btn_fireC <= key_pressed; // Space 'h12: btn_fireD <= key_pressed; // l-shift + 'h14: btn_fireC <= key_pressed; // ctrl + 'h11: btn_fireB <= key_pressed; // alt + 'h29: btn_fireA <= key_pressed; // Space 'h1A: btn_fireE <= key_pressed; // Z 'h22: btn_fireF <= key_pressed; // X 'h21: btn_fireG <= key_pressed; // C diff --git a/common/mist/sd_card.v b/common/mist/sd_card.v new file mode 100644 index 00000000..12c956a9 --- /dev/null +++ b/common/mist/sd_card.v @@ -0,0 +1,525 @@ +// +// sd_card.v +// +// This file implelents a sd card for the MIST board since on the board +// the SD card is connected to the ARM IO controller and the FPGA has no +// direct connection to the SD card. This file provides a SD card like +// interface to the IO controller easing porting of cores that expect +// a direct interface to the SD card. +// +// Copyright (c) 2014 Till Harbaum +// +// This source file is free software: you can redistribute it and/or modify +// it under the terms of the Lesser GNU General Public License as published +// by the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This source file is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// http://elm-chan.org/docs/mmc/mmc_e.html + +module sd_card ( + input clk_sys, + // link to user_io for io controller + output reg[31:0] sd_lba, + output reg sd_rd, + output reg sd_wr, + input sd_ack, + input sd_ack_conf, + output sd_conf, + output sd_sdhc, + + input img_mounted, + input [31:0] img_size, + + output reg sd_busy = 0, + // data coming in from io controller + input [7:0] sd_buff_dout, + input sd_buff_wr, + + // data going out to io controller + output [7:0] sd_buff_din, + + input [8:0] sd_buff_addr, + + // configuration input + input allow_sdhc, + + input sd_cs, + input sd_sck, + input sd_sdi, + output reg sd_sdo +); + +wire [31:0] OCR = { 1'b1, sd_sdhc, 6'h0, 9'h1f, 15'h0 }; // bit31 = finished powerup + // bit30 = 1 -> high capaciry card (sdhc) + // 15-23 supported voltage range +wire [7:0] READ_DATA_TOKEN = 8'hfe; + +// number of bytes to wait after a command before sending the reply +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; +reg [2:0] read_state = RD_STATE_IDLE; + +localparam WR_STATE_IDLE = 3'd0; +localparam WR_STATE_EXP_DTOKEN = 3'd1; +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; +reg [2:0] write_state = WR_STATE_IDLE; + +reg card_is_reset = 1'b0; // flag that card has received a reset command +reg [6:0] sbuf; +reg cmd55; +reg terminate_cmd = 1'b0; +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 [39:0] args; + +reg [7:0] reply; +reg [7:0] reply0, reply1, reply2, reply3; +reg [3:0] reply_len; + +// ------------------------- SECTOR BUFFER ----------------------- + +// the buffer itself. Can hold two sectors +reg [9:0] buffer_ptr; +wire [7:0] buffer_dout; +reg [7:0] buffer_din; +reg buffer_write_strobe; +reg sd_buff_sel; + +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), + .q_a (sd_buff_din), + + .clock_b (clk_sys), + .address_b (buffer_ptr), + .data_b (buffer_din), + .wren_b (buffer_write_strobe), + .q_b (buffer_dout) +); + +wire [7:0] WRITE_DATA_RESPONSE = 8'h05; + +// ------------------------- CSD/CID BUFFER ---------------------- +reg [7:0] conf; +assign sd_conf = sd_configuring; + +reg sd_configuring = 1; +reg [4:0] conf_buff_ptr; +reg [7:0] conf_byte; +reg[255:0] csdcid; + +// conf[0]==1 -> io controller is using an sdhc card +wire sd_has_sdhc = conf[0]; +assign sd_sdhc = allow_sdhc && sd_has_sdhc; + +always @(posedge clk_sys) begin + reg old_mounted; + + if (sd_buff_wr & sd_ack_conf) begin + if (sd_buff_addr == 32) begin + conf <= sd_buff_dout; + sd_configuring <= 0; + end + else csdcid[(31-sd_buff_addr) << 3 +:8] <= sd_buff_dout; + end + conf_byte <= csdcid[(31-conf_buff_ptr) << 3 +:8]; + + old_mounted <= img_mounted; + if (~old_mounted & img_mounted) 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] }; + else begin + // CSD V1.0 no. of blocks = c_size ** (c_size_mult + 2) + csdcid[49:47] <= 3'd7; //c_size_mult + csdcid[73:62] <= img_size[29:18]; //c_size + end + end +end + +always@(posedge clk_sys) begin + + reg old_sd_sck; + reg [5:0] ack; + + ack <= {ack[4:0], sd_ack}; + if(ack[5:4] == 'b01) { sd_rd, sd_wr } <= 2'b00; + if(ack[5:4] == 'b10) sd_busy <= 0; + + buffer_write_strobe <= 0; + if (buffer_write_strobe) buffer_ptr <= buffer_ptr + 1'd1; + + old_sd_sck <= sd_sck; + + // advance transmitter state machine on falling sck edge, so data is valid on the + // rising edge + // ----------------- spi transmitter -------------------- + if(sd_cs == 0 && old_sd_sck && ~sd_sck) begin + + sd_sdo <= 1'b1; // default: send 1's (busy/wait) + + if(byte_cnt == 5+NCR) begin + sd_sdo <= reply[~bit_cnt]; + + if(bit_cnt == 7) begin + // these three commands all have a reply_len of 0 and will thus + // not send more than a single reply byte + + // CMD9: SEND_CSD + // CMD10: SEND_CID + if((cmd == 8'h49)||(cmd == 8'h4a)) + read_state <= RD_STATE_SEND_TOKEN; // jump directly to data transmission + + // 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 + end + end + else if((reply_len > 0) && (byte_cnt == 5+NCR+1)) + sd_sdo <= reply0[~bit_cnt]; + else if((reply_len > 1) && (byte_cnt == 5+NCR+2)) + sd_sdo <= reply1[~bit_cnt]; + else if((reply_len > 2) && (byte_cnt == 5+NCR+3)) + sd_sdo <= reply2[~bit_cnt]; + else if((reply_len > 3) && (byte_cnt == 5+NCR+4)) + sd_sdo <= reply3[~bit_cnt]; + + // ---------- read state machine processing ------------- + + case(read_state) + RD_STATE_IDLE: ; + // don't do anything + + // 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; + read_state <= RD_STATE_IDLE; + end else if (bit_cnt == 7) + read_state <= RD_STATE_SEND_TOKEN; + end + end + + // send data token + RD_STATE_SEND_TOKEN: begin + if(~sd_busy) begin + sd_sdo <= READ_DATA_TOKEN[~bit_cnt]; + + if(bit_cnt == 7) begin + read_state <= RD_STATE_SEND_DATA; // next: send data + conf_buff_ptr <= (cmd == 8'h4a) ? 5'h0 : 5'h10; + end + end + end + + // send data + RD_STATE_SEND_DATA: begin + if(cmd == 8'h51 || (cmd == 8'h52 && !terminate_cmd)) // CMD17: READ_SINGLE_BLOCK, CMD18: READ_MULTIPLE_BLOCK + sd_sdo <= buffer_dout[~bit_cnt]; + else if(cmd == 8'h49 || cmd == 8'h4a) // CMD9: SEND_CSD, CMD10: SEND CID + sd_sdo <= conf_byte[~bit_cnt]; + + if(bit_cnt == 7) begin + + // sent 512 sector data bytes? + if((cmd == 8'h51) && &buffer_ptr[8:0]) + read_state <= RD_STATE_IDLE; // next: send crc. It's ignored so return to idle state + + if (cmd == 8'h52) begin + if (terminate_cmd) begin + read_state <= RD_STATE_IDLE; + cmd <= 0; + end else if (buffer_ptr[8:0] == 10) begin + // prefetch the next sector into the other buffer + sd_lba <= sd_lba + 1'd1; + sd_rd <= 1; + sd_busy <= 1; + sd_buff_sel <= !sd_buff_sel; + end else if (&buffer_ptr[8:0]) begin + delay_cnt <= 20; + read_state <= RD_STATE_DELAY; + end + end + + // sent 16 cid/csd data bytes? + if(((cmd == 8'h49)||(cmd == 8'h4a)) && conf_buff_ptr[3:0] == 4'h0f) // && (buffer_rptr == 16)) + read_state <= RD_STATE_IDLE; // return to idle state + + buffer_ptr <= buffer_ptr + 1'd1; + conf_buff_ptr<= conf_buff_ptr+ 1'd1; + end + end + + RD_STATE_DELAY: + if(bit_cnt == 7) begin + if (delay_cnt == 0) begin + read_state <= RD_STATE_SEND_TOKEN; + end else begin + delay_cnt <= delay_cnt - 1'd1; + end + end + + endcase + + // ------------------ write support ---------------------- + // send write data response + if(write_state == WR_STATE_SEND_DRESP) + sd_sdo <= WRITE_DATA_RESPONSE[~bit_cnt]; + + // busy after write until the io controller sends ack + if(write_state == WR_STATE_BUSY) + sd_sdo <= 1'b0; + end + + // spi receiver + // cs is active low + if(sd_cs == 1) begin + bit_cnt <= 3'd0; + terminate_cmd <= 0; + cmd <= 0; + read_state <= RD_STATE_IDLE; + end else if (~old_sd_sck & sd_sck) begin + bit_cnt <= bit_cnt + 3'd1; + + // assemble byte + if(bit_cnt != 7) + sbuf[6:0] <= { sbuf[5:0], sd_sdi }; + else begin + // finished reading one byte + // byte counter runs against 15 byte boundary + if(byte_cnt != 15) + byte_cnt <= byte_cnt + 4'd1; + + // 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) && + (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) + begin + byte_cnt <= 4'd0; + terminate_cmd <= 0; + if ({ sbuf, sd_sdi } == 8'h4c) begin + terminate_cmd <= 1; + end else + cmd <= { sbuf, sd_sdi}; + end + + // parse additional command bytes + if(byte_cnt == 0) args[39:32] <= { sbuf, sd_sdi}; + if(byte_cnt == 1) args[31:24] <= { sbuf, sd_sdi}; + if(byte_cnt == 2) args[23:16] <= { sbuf, sd_sdi}; + if(byte_cnt == 3) args[15:8] <= { sbuf, sd_sdi}; + if(byte_cnt == 4) args[7:0] <= { sbuf, sd_sdi}; + + // last byte received, evaluate + if(byte_cnt == 5) begin + + // default: + reply <= 8'h04; // illegal command + reply_len <= 4'd0; // no extra reply bytes + cmd55 <= 0; + + // CMD0: GO_IDLE_STATE + if(cmd == 8'h40) begin + card_is_reset <= 1'b1; + reply <= 8'h01; // ok, busy + end + + // every other command is only accepted after a reset + else if(card_is_reset) begin + // CMD12: STOP_TRANSMISSION + if (terminate_cmd) + reply <= 8'h00; // ok + else case(cmd) + // CMD1: SEND_OP_COND + 8'h41: reply <= 8'h00; // ok, not busy + + // CMD8: SEND_IF_COND (V2 only) + 8'h48: begin + reply <= 8'h01; // ok, busy + reply0 <= 8'h00; + reply1 <= 8'h00; + reply2 <= { 4'b0, args[19:16] }; + reply3 <= args[15:8]; + reply_len <= 4'd4; + end + + // CMD9: SEND_CSD + 8'h49: reply <= 8'h00; // ok + + // CMD10: SEND_CID + 8'h4a: reply <= 8'h00; // ok + + // CMD13: SEND_STATUS + 8'h4d: begin + reply <= 8'h00; // ok + reply0 <=8'h00; + reply_len <= 4'd1; + end + + // CMD16: SET_BLOCKLEN + 8'h50: + // we only support a block size of 512 + if(args[39:8] == 32'd512) + reply <= 8'h00; // ok + else + reply <= 8'h40; // parmeter error + + // CMD17: READ_SINGLE_BLOCK + 8'h51: reply <= 8'h00; // ok + + // CMD18: READ_MULTIPLE_BLOCK + 8'h52: reply <= 8'h00; // ok + + // CMD24: WRITE_BLOCK + 8'h58: begin + reply <= 8'h00; // ok + sd_lba <= sd_sdhc?args[39:8]:{9'd0, args[39:17]}; + write_state <= WR_STATE_EXP_DTOKEN; // expect data token + end + + // ACMD41: APP_SEND_OP_COND + 8'h69: if(cmd55) begin + reply <= 8'h00; // ok, not busy + end + + // CMD55: APP_COND + 8'h77: begin + reply <= 8'h01; // ok, busy + cmd55 <= 1; + end + + // CMD58: READ_OCR + 8'h7a: begin + reply <= 8'h00; // ok + + reply0 <= OCR[31:24]; // bit 30 = 1 -> high capacity card + reply1 <= OCR[23:16]; + reply2 <= OCR[15:8]; + 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 +end + +endmodule + +module sd_card_dpram #(parameter DATAWIDTH=8, ADDRWIDTH=9) +( + input clock_a, + input [ADDRWIDTH-1:0] address_a, + input [DATAWIDTH-1:0] data_a, + input wren_a, + output reg [DATAWIDTH-1:0] q_a, + + input clock_b, + input [ADDRWIDTH-1:0] address_b, + input [DATAWIDTH-1:0] data_b, + input wren_b, + output reg [DATAWIDTH-1:0] q_b +); + +reg [DATAWIDTH-1:0] ram[0:(1<