diff --git a/data/pins_sonata.xdc b/data/pins_sonata.xdc index b64c20d5..19fc8cd7 100644 --- a/data/pins_sonata.xdc +++ b/data/pins_sonata.xdc @@ -289,6 +289,15 @@ set_property -dict { PACKAGE_PIN H6 IOSTANDARD LVCMOS18 } [get_ports ethmac_int set_property PULLTYPE PULLUP [get_ports ethmac_intr] set_property -dict { PACKAGE_PIN H5 IOSTANDARD LVCMOS18 } [get_ports ethmac_cs] +## MicroSD card slot +set_property -dict { PACKAGE_PIN U6 IOSTANDARD LVCMOS33 } [get_ports microsd_clk] +set_property -dict { PACKAGE_PIN V4 IOSTANDARD LVCMOS33 } [get_ports microsd_dat0] +# set_property -dict { PACKAGE_PIN R7 IOSTANDARD LVCMOS33 } [get_ports microsd_dat1] // unused +# set_property -dict { PACKAGE_PIN V5 IOSTANDARD LVCMOS33 } [get_ports microsd_dat2] // unused +set_property -dict { PACKAGE_PIN T8 IOSTANDARD LVCMOS33 } [get_ports microsd_dat3] +set_property -dict { PACKAGE_PIN R8 IOSTANDARD LVCMOS33 } [get_ports microsd_cmd] +set_property -dict { PACKAGE_PIN B3 IOSTANDARD LVCMOS18 } [get_ports microsd_det] + # HyperRAM set_property -dict { PACKAGE_PIN B1 IOSTANDARD LVCMOS18 } [get_ports { hyperram_dq[0] }] set_property -dict { PACKAGE_PIN E2 IOSTANDARD LVCMOS18 } [get_ports { hyperram_dq[1] }] diff --git a/data/top_config.toml b/data/top_config.toml index 6e22489f..d30476c7 100644 --- a/data/top_config.toml +++ b/data/top_config.toml @@ -391,3 +391,16 @@ block_ios = [{block = "gpio", instance = 2, io = 0}] name = "pmod1" length = 8 block_ios = [{block = "gpio", instance = 2, io = 8}] + +## MicroSD card slot +[[pins]] +name = "microsd_clk" +block_ios = [{block = "spi", instance = 3, io = "sck"}] + +[[pins]] +name = "microsd_dat0" +block_ios = [{block = "spi", instance = 3, io = "rx"}] + +[[pins]] +name = "microsd_cmd" +block_ios = [{block = "spi", instance = 3, io = "tx"}] diff --git a/doc/ip/gpio.md b/doc/ip/gpio.md index 446d92fc..ccd90881 100644 --- a/doc/ip/gpio.md +++ b/doc/ip/gpio.md @@ -59,6 +59,7 @@ The only difference between the registers is that the latter has debounced signa | Bit offset | Description | |------------|-------------| +| 17 | MicroSD card detection (0: present, 1: absent) | | 16-14 | Software select switches (1, 2, 3) | | 13 | mikroBUS interrupt | | 12-5 | DIP switches | diff --git a/doc/ip/pinmux.md b/doc/ip/pinmux.md index 347f88a4..9fc4ab4a 100644 --- a/doc/ip/pinmux.md +++ b/doc/ip/pinmux.md @@ -92,6 +92,8 @@ The default value for all of these selectors is `'b10`. | 0x04d | pmod1[5] | 0, gpio_ios_i(2)(13) | | 0x04e | pmod1[6] | 0, gpio_ios_i(2)(14) | | 0x04f | pmod1[7] | 0, gpio_ios_i(2)(15) | +| 0x050 | microsd_clk | 0, spi_sck_i(3) | +| 0x051 | microsd_cmd | 0, spi_tx_i(3) | Besides the output pin selectors, there are also selectors for which pin should drive block inputs: @@ -105,7 +107,7 @@ Besides the output pin selectors, there are also selectors for which pin should | 0x805 | spi_rx_o(0) | 1'b0, appspi_d1, | | 0x806 | spi_rx_o(1) | 1'b0, 1'b0, | | 0x807 | spi_rx_o(2) | 1'b0, ethmac_cipo, | -| 0x808 | spi_rx_o(3) | 1'b0, rph_g9_cipo, ah_tmpio12, | +| 0x808 | spi_rx_o(3) | 1'b0, rph_g9_cipo, ah_tmpio12, microsd_dat0, | | 0x809 | spi_rx_o(4) | 1'b0, rph_g19_cipo, mb3, | | 0x80a | gpio_ios_o(0)(0) | 1'b0, rph_g0, | | 0x80b | gpio_ios_o(1)(0) | 1'b0, ah_tmpio0, | diff --git a/dv/verilator/top_verilator.sv b/dv/verilator/top_verilator.sv index 41ff30be..dd98ca0e 100644 --- a/dv/verilator/top_verilator.sv +++ b/dv/verilator/top_verilator.sv @@ -106,10 +106,12 @@ module top_verilator (input logic clk_i, rst_ni); wire ethmac_rst, ethmac_cs; // User LEDs. wire [7:0] usrLed; + // MicroSD card slot + wire microsd_dat3; // None of these signals is used presently. wire unused_io_ = ^{mb0, mb1, ah_tmpio10, rph_g18, rph_g17, rph_g16_ce2, rph_g8_ce0, rph_g7_ce1, ethmac_rst, ethmac_cs, - usrLed}; + usrLed, microsd_dat3}; // Reporting of CHERI enable/disable and any exceptions that occur. wire [CheriErrWidth-1:0] cheri_err; @@ -260,7 +262,9 @@ module top_verilator (input logic clk_i, rst_ni); .mb8(), .pmod0(), .pmod1(), - + .microsd_clk(), + .microsd_dat0(), + .microsd_cmd(), .tl_i(tlul_pkg::TL_H2D_DEFAULT), .tl_o() ); @@ -273,16 +277,17 @@ module top_verilator (input logic clk_i, rst_ni); logic gp_ah_tmpio10, gp_mb1; // CS outputs to SPI peripherals from controllers. - assign appspi_cs = spi_cs[0][0]; - assign lcd_cs = spi_cs[1][0]; - assign ethmac_cs = spi_cs[2][0]; - assign rph_g8_ce0 = spi_cs[3][0]; - assign rph_g7_ce1 = spi_cs[3][1]; - assign ah_tmpio10 = spi_cs[3][2]; - assign rph_g18 = spi_cs[4][0]; - assign rph_g17 = spi_cs[4][1]; - assign rph_g16_ce2 = spi_cs[4][2]; - assign mb1 = spi_cs[4][3]; + assign appspi_cs = spi_cs[0][0]; + assign lcd_cs = spi_cs[1][0]; + assign ethmac_cs = spi_cs[2][0]; + assign rph_g8_ce0 = spi_cs[3][0]; + assign rph_g7_ce1 = spi_cs[3][1]; + assign ah_tmpio10 = spi_cs[3][2]; + assign microsd_dat3 = spi_cs[3][3]; + assign rph_g18 = spi_cs[4][0]; + assign rph_g17 = spi_cs[4][1]; + assign rph_g16_ce2 = spi_cs[4][2]; + assign mb1 = spi_cs[4][3]; wire unused_gp_spi_cs_ = ^{gp_lcd_cs, gp_appspi_cs, gp_ethmac_cs, gp_rph_g8_ce0, gp_rph_g7_ce1, gp_rph_g18, gp_rph_g17, gp_rph_g16_ce2, diff --git a/rtl/fpga/top_sonata.sv b/rtl/fpga/top_sonata.sv index 4332635c..5aeae955 100644 --- a/rtl/fpga/top_sonata.sv +++ b/rtl/fpga/top_sonata.sv @@ -175,6 +175,15 @@ module top_sonata ( output logic appspi_d3, // HOLD_N or RESET_N output logic appspi_cs, // Chip select negated + // MicroSD card slot + output logic microsd_clk, // SPI mode: SCLK + input logic microsd_dat0, // SPI mode: CIPO +//input logic microsd_dat1, // SPI mode: NC +//input logic microsd_dat2, // SPI mode: NC + output logic microsd_dat3, // SPI mode: CS_N + output logic microsd_cmd, // SPI mode: COPI + input logic microsd_det, // Card insertion detection + inout wire [7:0] hyperram_dq, inout wire hyperram_rwds, output wire hyperram_ckp, @@ -342,6 +351,9 @@ module top_sonata ( .mb8, .pmod0, .pmod1, + .microsd_clk, + .microsd_dat0, + .microsd_cmd, .tl_i(tl_pinmux_h2d), .tl_o(tl_pinmux_d2h) ); @@ -354,16 +366,17 @@ module top_sonata ( logic gp_ah_tmpio10, gp_mb1; // CS outputs to SPI peripherals from controllers. - assign appspi_cs = spi_cs[0][0]; - assign lcd_cs = spi_cs[1][0]; - assign ethmac_cs = spi_cs[2][0]; - assign rph_g8_ce0 = spi_cs[3][0]; - assign rph_g7_ce1 = spi_cs[3][1]; - assign ah_tmpio10 = spi_cs[3][2]; - assign rph_g18 = spi_cs[4][0]; - assign rph_g17 = spi_cs[4][1]; - assign rph_g16_ce2 = spi_cs[4][2]; - assign mb1 = spi_cs[4][3]; + assign appspi_cs = spi_cs[0][0]; + assign lcd_cs = spi_cs[1][0]; + assign ethmac_cs = spi_cs[2][0]; + assign rph_g8_ce0 = spi_cs[3][0]; + assign rph_g7_ce1 = spi_cs[3][1]; + assign ah_tmpio10 = spi_cs[3][2]; + assign microsd_dat3 = spi_cs[3][3]; + assign rph_g18 = spi_cs[4][0]; + assign rph_g17 = spi_cs[4][1]; + assign rph_g16_ce2 = spi_cs[4][2]; + assign mb1 = spi_cs[4][3]; wire unused_gp_spi_cs_ = ^{gp_lcd_cs, gp_appspi_cs, gp_ethmac_cs, gp_rph_g8_ce0, gp_rph_g7_ce1, @@ -373,8 +386,7 @@ module top_sonata ( // Collect the unused CS lines. wire unused_spi_cs_ = ^{spi_cs[0][SPI_CS_NUM-1:1], spi_cs[1][SPI_CS_NUM-1:1], - spi_cs[2][SPI_CS_NUM-1:1], - spi_cs[3][SPI_CS_NUM-1]}; + spi_cs[2][SPI_CS_NUM-1:1]}; // Enable CHERI by default. logic enable_cheri; @@ -407,7 +419,8 @@ module top_sonata ( // GPIO .gp_i ({ - 15'b0, + 14'b0, + microsd_det, // MicroSD card insertion detection sel_sw_n, // Software selection switches mb9, // mikroBUS Click interrupt user_sw_n, // user switches @@ -458,6 +471,7 @@ module top_sonata ( // - 2x Raspberry Pi HAT // - Arduino Shield // - mikroBUS Click + // - MicroSD card slot .spi_rx_i (spi_rx), .spi_tx_o (spi_tx), .spi_cs_o (spi_cs), diff --git a/rtl/system/pinmux.sv b/rtl/system/pinmux.sv index 53f6a607..f285e419 100644 --- a/rtl/system/pinmux.sv +++ b/rtl/system/pinmux.sv @@ -100,6 +100,9 @@ module pinmux ( inout logic mb8, inout logic [7:0] pmod0, inout logic [7:0] pmod1, + inout logic microsd_clk, + inout logic microsd_dat0, + inout logic microsd_cmd, // TileLink interfaces. input tlul_pkg::tl_h2d_t tl_i, @@ -4335,6 +4338,110 @@ module pinmux ( assign pmod1[7] = pmod1_7_en_o ? pmod1_7_o : 1'bz; + logic [1:0] microsd_clk_sel; + logic microsd_clk_o; + logic microsd_clk_en_o; + logic microsd_clk_sel_addressed; + + // Register addresses of 0x000 to 0x7ff are pin selectors, which are packed with 4 per 32-bit word. + assign microsd_clk_sel_addressed = + reg_addr[RegAddrWidth-1] == 1'b0 & + reg_addr[RegAddrWidth-2:0] == 80 & + reg_be[0] == 1'b1; + + always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + // Select second input by default so that pins are connected to the first block that is specified in the configuration. + microsd_clk_sel <= 2'b10; + end else begin + if (reg_we & microsd_clk_sel_addressed) begin + microsd_clk_sel <= reg_wdata[0+:2]; + end + end + end + + prim_onehot_mux #( + .Width(1), + .Inputs(2) + ) microsd_clk_mux ( + .clk_i, + .rst_ni, + .in_i({ + 1'b0, // This is set to Z later when output enable is low. + spi_sck_i[3] + }), + .sel_i(microsd_clk_sel), + .out_o(microsd_clk_o) + ); + + prim_onehot_mux #( + .Width(1), + .Inputs(2) + ) microsd_clk_enable_mux ( + .clk_i, + .rst_ni, + .in_i({ + 1'b0, + '1 + }), + .sel_i(microsd_clk_sel), + .out_o(microsd_clk_en_o) + ); + + assign microsd_clk = microsd_clk_en_o ? microsd_clk_o : 1'bz; + + logic [1:0] microsd_cmd_sel; + logic microsd_cmd_o; + logic microsd_cmd_en_o; + logic microsd_cmd_sel_addressed; + + // Register addresses of 0x000 to 0x7ff are pin selectors, which are packed with 4 per 32-bit word. + assign microsd_cmd_sel_addressed = + reg_addr[RegAddrWidth-1] == 1'b0 & + reg_addr[RegAddrWidth-2:0] == 80 & + reg_be[1] == 1'b1; + + always @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + // Select second input by default so that pins are connected to the first block that is specified in the configuration. + microsd_cmd_sel <= 2'b10; + end else begin + if (reg_we & microsd_cmd_sel_addressed) begin + microsd_cmd_sel <= reg_wdata[8+:2]; + end + end + end + + prim_onehot_mux #( + .Width(1), + .Inputs(2) + ) microsd_cmd_mux ( + .clk_i, + .rst_ni, + .in_i({ + 1'b0, // This is set to Z later when output enable is low. + spi_tx_i[3] + }), + .sel_i(microsd_cmd_sel), + .out_o(microsd_cmd_o) + ); + + prim_onehot_mux #( + .Width(1), + .Inputs(2) + ) microsd_cmd_enable_mux ( + .clk_i, + .rst_ni, + .in_i({ + 1'b0, + '1 + }), + .sel_i(microsd_cmd_sel), + .out_o(microsd_cmd_en_o) + ); + + assign microsd_cmd = microsd_cmd_en_o ? microsd_cmd_o : 1'bz; + // Inputs - Physical pin inputs are muxed to particular block IO logic [1:0] uart_rx_0_sel; @@ -4609,7 +4716,7 @@ module pinmux ( .out_o(spi_rx_o[2]) ); - logic [2:0] spi_rx_3_sel; + logic [3:0] spi_rx_3_sel; logic spi_rx_3_sel_addressed; // Register addresses of 0x800 to 0xfff are block IO selectors, which are packed with 4 per 32-bit word. @@ -4621,24 +4728,25 @@ module pinmux ( always @(posedge clk_i or negedge rst_ni) begin if (!rst_ni) begin // Select second input by default so that pins are connected to the first block that is specified in the configuration. - spi_rx_3_sel <= 3'b10; + spi_rx_3_sel <= 4'b10; end else begin if (reg_we & spi_rx_3_sel_addressed) begin - spi_rx_3_sel <= reg_wdata[0+:3]; + spi_rx_3_sel <= reg_wdata[0+:4]; end end end prim_onehot_mux #( .Width(1), - .Inputs(3) + .Inputs(4) ) spi_rx_3_mux ( .clk_i, .rst_ni, .in_i({ 1'b0, rph_g9_cipo, - ah_tmpio12 + ah_tmpio12, + microsd_dat0 }), .sel_i(spi_rx_3_sel), .out_o(spi_rx_o[3])