// SPDX-FileCopyrightText: 2020 Efabless Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // SPDX-License-Identifier: Apache-2.0 `default_nettype none //----------------------------------------------------------- // Housekeeping interface for Caravel //----------------------------------------------------------- // Written by Tim Edwards // efabless, inc. September 27, 2020 //----------------------------------------------------------- //----------------------------------------------------------- // This is a standalone slave SPI for the caravel chip that is // intended to be independent of the picosoc and independent // of all IP blocks except the power-on-reset. This SPI has // register outputs controlling the functions that critically // affect operation of the picosoc and so cannot be accessed // from the picosoc itself. This includes the PLL enables, // mode, and trim. It also has a general reset for the picosoc, // an IRQ input, a bypass for the entire crystal oscillator // and PLL chain, the manufacturer and product IDs and product // revision number. // // Updated and revised, 10/13/2021: // This module now comprises what was previously split into // the housekeeping SPI, the mprj_ctrl block (control over // the GPIO), and sysctrl (redirection of certain internal // signals to the GPIO); and additionally manages the SPI // flash signals and pass-through mode. Essentially all // aspects of the system related to the use and configuration // of the GPIO has been shifted to this module. This allows // GPIO to be configured from either the management SoC // through the wishbone interface, or externally through the // SPI interface. It allows essentially any processor to // take the place of the PicoRV32 as long as that processor // can access memory-mapped space via the wishbone bus. //----------------------------------------------------------- //------------------------------------------------------------ // Caravel defined registers (by SPI address): // See: doc/memory_map.txt //------------------------------------------------------------ module housekeeping #( parameter GPIO_BASE_ADR = 32'h2600_0000, parameter SPI_BASE_ADR = 32'h2610_0000, parameter SYS_BASE_ADR = 32'h2620_0000, parameter IO_CTRL_BITS = 13 ) ( `ifdef USE_POWER_PINS inout VPWR, inout VGND, `endif // Wishbone interface to management SoC input wb_clk_i, input wb_rstn_i, input [31:0] wb_adr_i, input [31:0] wb_dat_i, input [3:0] wb_sel_i, input wb_we_i, input wb_cyc_i, input wb_stb_i, output wb_ack_o, output [31:0] wb_dat_o, // Primary reset input porb, // Clocking control parameters output pll_ena, output pll_dco_ena, output [4:0] pll_div, output [2:0] pll_sel, output [2:0] pll90_sel, output [25:0] pll_trim, output pll_bypass, // Module enable status from SoC input qspi_enabled, // Flash SPI is in quad mode input uart_enabled, // UART is enabled input spi_enabled, // SPI master is enabled input debug_mode, // Debug mode enabled // UART interface to/from SoC input ser_tx, output ser_rx, // SPI master interface to/from SoC output spi_sdi, input spi_csb, input spi_sck, input spi_sdo, input spi_sdoenb, // External (originating from SPI and pad) IRQ and reset output [2:0] irq, output reset, // GPIO serial loader programming interface output serial_clock, output serial_load, output serial_resetn, output serial_data_1, output serial_data_2, // GPIO data management (to padframe)---three-pin interface input [`MPRJ_IO_PADS-1:0] mgmt_gpio_in, output [`MPRJ_IO_PADS-1:0] mgmt_gpio_out, output [`MPRJ_IO_PADS-1:0] mgmt_gpio_oeb, // Power control output (reserved for future use with LDOs) output [`MPRJ_PWR_PADS-1:0] pwr_ctrl_out, // CPU trap state status (for system monitoring) input trap, // User clock (for system monitoring) input user_clock, // Mask revision/User project ID input [31:0] mask_rev_in, // SPI flash management (management SoC side) input spimemio_flash_csb, input spimemio_flash_clk, input spimemio_flash_io0_oeb, input spimemio_flash_io1_oeb, input spimemio_flash_io2_oeb, input spimemio_flash_io3_oeb, input spimemio_flash_io0_do, input spimemio_flash_io1_do, input spimemio_flash_io2_do, input spimemio_flash_io3_do, output spimemio_flash_io0_di, output spimemio_flash_io1_di, output spimemio_flash_io2_di, output spimemio_flash_io3_di, // Debug interface (routes to first GPIO) from management SoC output debug_in, input debug_out, input debug_oeb, // SPI flash management (padframe side) // (io2 and io3 are part of GPIO array, not dedicated pads) output pad_flash_csb, output pad_flash_csb_oeb, output pad_flash_clk, output pad_flash_clk_oeb, output pad_flash_io0_oeb, output pad_flash_io1_oeb, output pad_flash_io0_ieb, output pad_flash_io1_ieb, output pad_flash_io0_do, output pad_flash_io1_do, input pad_flash_io0_di, input pad_flash_io1_di, `ifdef USE_SRAM_RO_INTERFACE output sram_ro_clk, output sram_ro_csb, output [7:0] sram_ro_addr, input [31:0] sram_ro_data, `endif // System signal monitoring input usr1_vcc_pwrgood, input usr2_vcc_pwrgood, input usr1_vdd_pwrgood, input usr2_vdd_pwrgood ); localparam OEB = 1; // Offset of output enable (bar) in shift register localparam INP_DIS = 3; // Offset of input disable in shift register reg [25:0] pll_trim; reg [4:0] pll_div; reg [2:0] pll_sel; reg [2:0] pll90_sel; reg pll_dco_ena; reg pll_ena; reg pll_bypass; reg reset_reg; reg irq_spi; reg serial_bb_clock; reg serial_bb_load; reg serial_bb_resetn; reg serial_bb_data_1; reg serial_bb_data_2; reg serial_bb_enable; reg serial_xfer; reg hkspi_disable; `ifdef USE_SRAM_RO_INTERFACE reg sram_ro_clk; reg sram_ro_csb; reg [7:0] sram_ro_addr; `endif reg clk1_output_dest; reg clk2_output_dest; reg trap_output_dest; reg irq_1_inputsrc; reg irq_2_inputsrc; reg [IO_CTRL_BITS-1:0] gpio_configure [`MPRJ_IO_PADS-1:0]; reg [`MPRJ_IO_PADS-1:0] mgmt_gpio_data; reg [`MPRJ_PWR_PADS-1:0] pwr_ctrl_out; /* mgmt_gpio_data_buf holds the lower bits during a back-door * write to GPIO data so that all 32 bits can update at once. */ reg [23:0] mgmt_gpio_data_buf; wire usr1_vcc_pwrgood; wire usr2_vcc_pwrgood; wire usr1_vdd_pwrgood; wire usr2_vdd_pwrgood; wire [7:0] odata; wire [7:0] idata; wire [7:0] iaddr; wire [2:0] irq; wire trap; wire rdstb; wire wrstb; wire pass_thru_mgmt; // Mode detected by housekeeping_spi wire pass_thru_mgmt_delay; wire pass_thru_user; // Mode detected by housekeeping_spi wire pass_thru_user_delay; wire pass_thru_mgmt_reset; wire pass_thru_user_reset; wire sdo; wire sdo_enb; wire [7:0] caddr; // Combination of SPI address and back door address wire [7:0] cdata; // Combination of SPI data and back door data wire cwstb; // Combination of SPI write strobe and back door write strobe wire csclk; // Combination of SPI SCK and back door access trigger // Output clock signals buffer wires wire mgmt_gpio_out_9_prebuff, mgmt_gpio_out_14_prebuff, mgmt_gpio_out_15_prebuff, pad_flash_clk_prebuff; `ifdef USE_SRAM_RO_INTERFACE wire [31:0] sram_ro_data; `endif // Housekeeping side 3-wire interface to GPIOs (see below) wire [`MPRJ_IO_PADS-1:0] mgmt_gpio_out; // Pass-through mode handling. Signals may only be applied when the // core processor is in reset. assign reset = (pass_thru_mgmt_reset) ? 1'b1 : reset_reg; // Invert wb_rstn_i wire wb_rst_i; assign wb_rst_i = ~wb_rstn_i; // Pass-through mode. Housekeeping SPI signals get inserted // between the management SoC and the flash SPI I/O. assign pad_flash_csb = (pass_thru_mgmt_delay) ? mgmt_gpio_in[3] : spimemio_flash_csb; assign pad_flash_csb_oeb = (pass_thru_mgmt_delay) ? 1'b0 : (~porb ? 1'b1 : 1'b0); assign pad_flash_clk_prebuff = (pass_thru_mgmt) ? mgmt_gpio_in[4] : spimemio_flash_clk; assign pad_flash_clk_oeb = (pass_thru_mgmt) ? 1'b0 : (~porb ? 1'b1 : 1'b0); assign pad_flash_io0_oeb = (pass_thru_mgmt_delay) ? 1'b0 : spimemio_flash_io0_oeb; assign pad_flash_io1_oeb = (pass_thru_mgmt) ? 1'b1 : spimemio_flash_io1_oeb; assign pad_flash_io0_ieb = (pass_thru_mgmt_delay) ? 1'b1 : ~spimemio_flash_io0_oeb; assign pad_flash_io1_ieb = (pass_thru_mgmt) ? 1'b0 : ~spimemio_flash_io1_oeb; assign pad_flash_io0_do = (pass_thru_mgmt_delay) ? mgmt_gpio_in[2] : spimemio_flash_io0_do; assign pad_flash_io1_do = spimemio_flash_io1_do; assign spimemio_flash_io0_di = (pass_thru_mgmt_delay) ? 1'b0 : pad_flash_io0_di; assign spimemio_flash_io1_di = (pass_thru_mgmt) ? 1'b0 : pad_flash_io1_di; (* keep *) sky130_fd_sc_hd__clkbuf_8 pad_flashh_clk_buff_inst ( `ifdef USE_POWER_PINS .VPWR(VPWR), .VGND(VGND), .VPB(VPWR), .VNB(VGND), `endif .A(pad_flash_clk_prebuff), .X(pad_flash_clk)); wire [11:0] mfgr_id; wire [7:0] prod_id; wire [31:0] mask_rev; reg serial_busy; // Wishbone bus "back door" to SPI registers. This section of code // (1) Maps SPI byte addresses to memory map 32-bit addresses // (2) Applies signals to the housekeeping SPI to mux in the SPI address, // clock, and write strobe. This is done carefully and slowly to // avoid glitching on the SCK line and to avoid forcing the // housekeeping module to keep up with the core clock timing. wire sys_select; // System monitoring memory map address selected wire gpio_select; // GPIO configuration memory map address selected wire spi_select; // SPI back door memory map address selected // Wishbone Back Door. This is a simple interface making use of the // housekeeping SPI protocol. The housekeeping SPI uses byte-wide // data, so this interface will stall the processor by holding wb_ack_o // low until all bytes have been transferred between the processor and // housekeeping SPI. reg [3:0] wbbd_state; reg [7:0] wbbd_addr; /* SPI address translated from WB */ reg [7:0] wbbd_data; /* SPI data translated from WB */ reg wbbd_sck; /* wishbone access trigger (back-door clock) */ reg wbbd_write; /* wishbone write trigger (back-door strobe) */ reg wbbd_busy; /* Raised during a wishbone read or write */ reg wb_ack_o; /* acknowledge signal back to wishbone bus */ reg [31:0] wb_dat_o; /* data output to wishbone bus */ // This defines a state machine that accesses the SPI registers through // the back door wishbone interface. The process is relatively slow // since the SPI data are byte-wide, so four individual accesses are // made to read 4 bytes from the SPI to fill data on the wishbone bus // before sending ACK and letting the processor continue. `define WBBD_IDLE 4'h0 /* Back door access is idle */ `define WBBD_SETUP0 4'h1 /* Apply address and data for byte 1 of 4 */ `define WBBD_RW0 4'h2 /* Latch data for byte 1 of 4 */ `define WBBD_SETUP1 4'h3 /* Apply address and data for byte 2 of 4 */ `define WBBD_RW1 4'h4 /* Latch data for byte 2 of 4 */ `define WBBD_SETUP2 4'h5 /* Apply address and data for byte 3 of 4 */ `define WBBD_RW2 4'h6 /* Latch data for byte 3 of 4 */ `define WBBD_SETUP3 4'h7 /* Apply address and data for byte 4 of 4 */ `define WBBD_RW3 4'h8 /* Latch data for byte 4 of 4 */ `define WBBD_DONE 4'h9 /* Send ACK back to wishbone */ assign sys_select = (wb_adr_i[31:8] == SYS_BASE_ADR[31:8]); assign gpio_select = (wb_adr_i[31:8] == GPIO_BASE_ADR[31:8]); assign spi_select = (wb_adr_i[31:8] == SPI_BASE_ADR[31:8]); /* Register bit to SPI address mapping */ function [7:0] fdata(input [7:0] address); begin case (address) /* Housekeeping SPI Protocol */ 8'h00 : fdata = 8'h00; // SPI status (fixed) /* Status and Identification */ 8'h01 : fdata = {4'h0, mfgr_id[11:8]}; // Manufacturer ID (fixed) 8'h02 : fdata = mfgr_id[7:0]; // Manufacturer ID (fixed) 8'h03 : fdata = prod_id; // Product ID (fixed) 8'h04 : fdata = mask_rev[31:24]; // Mask rev (via programmed) 8'h05 : fdata = mask_rev[23:16]; // Mask rev (via programmed) 8'h06 : fdata = mask_rev[15:8]; // Mask rev (via programmed) 8'h07 : fdata = mask_rev[7:0]; // Mask rev (via programmed) /* Clocking control */ 8'h08 : fdata = {6'b000000, pll_dco_ena, pll_ena}; 8'h09 : fdata = {7'b0000000, pll_bypass}; 8'h0a : fdata = {7'b0000000, irq_spi}; 8'h0b : fdata = {7'b0000000, reset}; 8'h0c : fdata = {7'b0000000, trap}; // CPU trap state 8'h0d : fdata = pll_trim[7:0]; 8'h0e : fdata = pll_trim[15:8]; 8'h0f : fdata = pll_trim[23:16]; 8'h10 : fdata = {6'b000000, pll_trim[25:24]}; 8'h11 : fdata = {2'b00, pll90_sel, pll_sel}; 8'h12 : fdata = {3'b000, pll_div}; // GPIO Control (bit bang and automatic) // NOTE: "serial_busy" is the read-back signal occupying the same // address/bit as "serial_xfer". 8'h13 : fdata = {1'b0, serial_data_2, serial_data_1, serial_bb_clock, serial_bb_load, serial_bb_resetn, serial_bb_enable, serial_busy}; `ifdef USE_SRAM_RO_INTERFACE /* Optional: SRAM read-only port (registers 14 to 19) */ 8'h14 : fdata = {6'b000000, sram_ro_clk, sram_ro_csb}; 8'h15 : fdata = sram_ro_addr; 8'h16 : fdata = sram_ro_data[31:24]; 8'h17 : fdata = sram_ro_data[23:16]; 8'h18 : fdata = sram_ro_data[15:8]; 8'h19 : fdata = sram_ro_data[7:0]; `endif /* System monitoring */ 8'h1a : fdata = {4'b0000, usr1_vcc_pwrgood, usr2_vcc_pwrgood, usr1_vdd_pwrgood, usr2_vdd_pwrgood}; 8'h1b : fdata = {5'b00000, clk1_output_dest, clk2_output_dest, trap_output_dest}; 8'h1c : fdata = {6'b000000, irq_2_inputsrc, irq_1_inputsrc}; /* GPIO Configuration */ 8'h1d : fdata = {3'b000, gpio_configure[0][12:8]}; 8'h1e : fdata = gpio_configure[0][7:0]; 8'h1f : fdata = {3'b000, gpio_configure[1][12:8]}; 8'h20 : fdata = gpio_configure[1][7:0]; 8'h21 : fdata = {3'b000, gpio_configure[2][12:8]}; 8'h22 : fdata = gpio_configure[2][7:0]; 8'h23 : fdata = {3'b000, gpio_configure[3][12:8]}; 8'h24 : fdata = gpio_configure[3][7:0]; 8'h25 : fdata = {3'b000, gpio_configure[4][12:8]}; 8'h26 : fdata = gpio_configure[4][7:0]; 8'h27 : fdata = {3'b000, gpio_configure[5][12:8]}; 8'h28 : fdata = gpio_configure[5][7:0]; 8'h29 : fdata = {3'b000, gpio_configure[6][12:8]}; 8'h2a : fdata = gpio_configure[6][7:0]; 8'h2b : fdata = {3'b000, gpio_configure[7][12:8]}; 8'h2c : fdata = gpio_configure[7][7:0]; 8'h2d : fdata = {3'b000, gpio_configure[8][12:8]}; 8'h2e : fdata = gpio_configure[8][7:0]; 8'h2f : fdata = {3'b000, gpio_configure[9][12:8]}; 8'h30 : fdata = gpio_configure[9][7:0]; 8'h31 : fdata = {3'b000, gpio_configure[10][12:8]}; 8'h32 : fdata = gpio_configure[10][7:0]; 8'h33 : fdata = {3'b000, gpio_configure[11][12:8]}; 8'h34 : fdata = gpio_configure[11][7:0]; 8'h35 : fdata = {3'b000, gpio_configure[12][12:8]}; 8'h36 : fdata = gpio_configure[12][7:0]; 8'h37 : fdata = {3'b000, gpio_configure[13][12:8]}; 8'h38 : fdata = gpio_configure[13][7:0]; 8'h39 : fdata = {3'b000, gpio_configure[14][12:8]}; 8'h3a : fdata = gpio_configure[14][7:0]; 8'h3b : fdata = {3'b000, gpio_configure[15][12:8]}; 8'h3c : fdata = gpio_configure[15][7:0]; 8'h3d : fdata = {3'b000, gpio_configure[16][12:8]}; 8'h3e : fdata = gpio_configure[16][7:0]; 8'h3f : fdata = {3'b000, gpio_configure[17][12:8]}; 8'h40 : fdata = gpio_configure[17][7:0]; 8'h41 : fdata = {3'b000, gpio_configure[18][12:8]}; 8'h42 : fdata = gpio_configure[18][7:0]; 8'h43 : fdata = {3'b000, gpio_configure[19][12:8]}; 8'h44 : fdata = gpio_configure[19][7:0]; 8'h45 : fdata = {3'b000, gpio_configure[20][12:8]}; 8'h46 : fdata = gpio_configure[20][7:0]; 8'h47 : fdata = {3'b000, gpio_configure[21][12:8]}; 8'h48 : fdata = gpio_configure[21][7:0]; 8'h49 : fdata = {3'b000, gpio_configure[22][12:8]}; 8'h4a : fdata = gpio_configure[22][7:0]; 8'h4b : fdata = {3'b000, gpio_configure[23][12:8]}; 8'h4c : fdata = gpio_configure[23][7:0]; 8'h4d : fdata = {3'b000, gpio_configure[24][12:8]}; 8'h4e : fdata = gpio_configure[24][7:0]; 8'h4f : fdata = {3'b000, gpio_configure[25][12:8]}; 8'h50 : fdata = gpio_configure[25][7:0]; 8'h51 : fdata = {3'b000, gpio_configure[26][12:8]}; 8'h52 : fdata = gpio_configure[26][7:0]; 8'h53 : fdata = {3'b000, gpio_configure[27][12:8]}; 8'h54 : fdata = gpio_configure[27][7:0]; 8'h55 : fdata = {3'b000, gpio_configure[28][12:8]}; 8'h56 : fdata = gpio_configure[28][7:0]; 8'h57 : fdata = {3'b000, gpio_configure[29][12:8]}; 8'h58 : fdata = gpio_configure[29][7:0]; 8'h59 : fdata = {3'b000, gpio_configure[30][12:8]}; 8'h5a : fdata = gpio_configure[30][7:0]; 8'h5b : fdata = {3'b000, gpio_configure[31][12:8]}; 8'h5c : fdata = gpio_configure[31][7:0]; 8'h5d : fdata = {3'b000, gpio_configure[32][12:8]}; 8'h5e : fdata = gpio_configure[32][7:0]; 8'h5f : fdata = {3'b000, gpio_configure[33][12:8]}; 8'h60 : fdata = gpio_configure[33][7:0]; 8'h61 : fdata = {3'b000, gpio_configure[34][12:8]}; 8'h62 : fdata = gpio_configure[34][7:0]; 8'h63 : fdata = {3'b000, gpio_configure[35][12:8]}; 8'h64 : fdata = gpio_configure[35][7:0]; 8'h65 : fdata = {3'b000, gpio_configure[36][12:8]}; 8'h66 : fdata = gpio_configure[36][7:0]; 8'h67 : fdata = {3'b000, gpio_configure[37][12:8]}; 8'h68 : fdata = gpio_configure[37][7:0]; // GPIO Data 8'h69 : fdata = {2'b00, mgmt_gpio_in[`MPRJ_IO_PADS-1:32]}; 8'h6a : fdata = mgmt_gpio_in[31:24]; 8'h6b : fdata = mgmt_gpio_in[23:16]; 8'h6c : fdata = mgmt_gpio_in[15:8]; 8'h6d : fdata = mgmt_gpio_in[7:0]; // Power Control (reserved) 8'h6e : fdata = {4'b0000, pwr_ctrl_out}; // Housekeeping SPI system disable 8'h6f : fdata = {7'b0000000, hkspi_disable}; default: fdata = 8'h00; endcase end endfunction /* Memory map address to SPI address translation for back door access */ /* (see doc/memory_map.txt) */ wire [11:0] gpio_adr = GPIO_BASE_ADR[23:12]; wire [11:0] sys_adr = SYS_BASE_ADR[23:12]; wire [11:0] spi_adr = SPI_BASE_ADR[23:12]; function [7:0] spiaddr(input [31:0] wbaddress); begin /* Address taken from lower 8 bits and upper 4 bits of the 32-bit */ /* wishbone address. */ case ({wbaddress[23:20], wbaddress[7:0]}) spi_adr | 12'h000 : spiaddr = 8'h00; // SPI status (reserved) spi_adr | 12'h004 : spiaddr = 8'h03; // product ID spi_adr | 12'h005 : spiaddr = 8'h02; // Manufacturer ID (low) spi_adr | 12'h006 : spiaddr = 8'h01; // Manufacturer ID (high) spi_adr | 12'h008 : spiaddr = 8'h07; // User project ID (low) spi_adr | 12'h009 : spiaddr = 8'h06; // User project ID . spi_adr | 12'h00a : spiaddr = 8'h05; // User project ID . spi_adr | 12'h00b : spiaddr = 8'h04; // User project ID (high) spi_adr | 12'h00c : spiaddr = 8'h08; // PLL enables spi_adr | 12'h010 : spiaddr = 8'h09; // PLL bypass spi_adr | 12'h014 : spiaddr = 8'h0a; // IRQ spi_adr | 12'h018 : spiaddr = 8'h0b; // Reset spi_adr | 12'h028 : spiaddr = 8'h0c; // CPU trap state spi_adr | 12'h01f : spiaddr = 8'h10; // PLL trim spi_adr | 12'h01e : spiaddr = 8'h0f; // PLL trim spi_adr | 12'h01d : spiaddr = 8'h0e; // PLL trim spi_adr | 12'h01c : spiaddr = 8'h0d; // PLL trim spi_adr | 12'h020 : spiaddr = 8'h11; // PLL source spi_adr | 12'h024 : spiaddr = 8'h12; // PLL divider spi_adr | 12'h02c : spiaddr = 8'h19; // SRAM read-only data spi_adr | 12'h02d : spiaddr = 8'h18; // SRAM read-only data spi_adr | 12'h02e : spiaddr = 8'h17; // SRAM read-only data spi_adr | 12'h02f : spiaddr = 8'h16; // SRAM read-only data spi_adr | 12'h030 : spiaddr = 8'h15; // SRAM read-only address spi_adr | 12'h034 : spiaddr = 8'h14; // SRAM read-only control gpio_adr | 12'h000 : spiaddr = 8'h13; // GPIO control sys_adr | 12'h000 : spiaddr = 8'h1a; // Power monitor sys_adr | 12'h004 : spiaddr = 8'h1b; // Output redirect sys_adr | 12'h00c : spiaddr = 8'h1c; // Input redirect gpio_adr | 12'h025 : spiaddr = 8'h1d; // GPIO configuration gpio_adr | 12'h024 : spiaddr = 8'h1e; gpio_adr | 12'h029 : spiaddr = 8'h1f; gpio_adr | 12'h028 : spiaddr = 8'h20; gpio_adr | 12'h02d : spiaddr = 8'h21; gpio_adr | 12'h02c : spiaddr = 8'h22; gpio_adr | 12'h031 : spiaddr = 8'h23; gpio_adr | 12'h030 : spiaddr = 8'h24; gpio_adr | 12'h035 : spiaddr = 8'h25; gpio_adr | 12'h034 : spiaddr = 8'h26; gpio_adr | 12'h039 : spiaddr = 8'h27; gpio_adr | 12'h038 : spiaddr = 8'h28; gpio_adr | 12'h03d : spiaddr = 8'h29; gpio_adr | 12'h03c : spiaddr = 8'h2a; gpio_adr | 12'h041 : spiaddr = 8'h2b; gpio_adr | 12'h040 : spiaddr = 8'h2c; gpio_adr | 12'h045 : spiaddr = 8'h2d; gpio_adr | 12'h044 : spiaddr = 8'h2e; gpio_adr | 12'h049 : spiaddr = 8'h2f; gpio_adr | 12'h048 : spiaddr = 8'h30; gpio_adr | 12'h04d : spiaddr = 8'h31; gpio_adr | 12'h04c : spiaddr = 8'h32; gpio_adr | 12'h051 : spiaddr = 8'h33; gpio_adr | 12'h050 : spiaddr = 8'h34; gpio_adr | 12'h055 : spiaddr = 8'h35; gpio_adr | 12'h054 : spiaddr = 8'h36; gpio_adr | 12'h059 : spiaddr = 8'h37; gpio_adr | 12'h058 : spiaddr = 8'h38; gpio_adr | 12'h05d : spiaddr = 8'h39; gpio_adr | 12'h05c : spiaddr = 8'h3a; gpio_adr | 12'h061 : spiaddr = 8'h3b; gpio_adr | 12'h060 : spiaddr = 8'h3c; gpio_adr | 12'h065 : spiaddr = 8'h3d; gpio_adr | 12'h064 : spiaddr = 8'h3e; gpio_adr | 12'h069 : spiaddr = 8'h3f; gpio_adr | 12'h068 : spiaddr = 8'h40; gpio_adr | 12'h06d : spiaddr = 8'h41; gpio_adr | 12'h06c : spiaddr = 8'h42; gpio_adr | 12'h071 : spiaddr = 8'h43; gpio_adr | 12'h070 : spiaddr = 8'h44; gpio_adr | 12'h075 : spiaddr = 8'h45; gpio_adr | 12'h074 : spiaddr = 8'h46; gpio_adr | 12'h079 : spiaddr = 8'h47; gpio_adr | 12'h078 : spiaddr = 8'h48; gpio_adr | 12'h07d : spiaddr = 8'h49; gpio_adr | 12'h07c : spiaddr = 8'h4a; gpio_adr | 12'h081 : spiaddr = 8'h4b; gpio_adr | 12'h080 : spiaddr = 8'h4c; gpio_adr | 12'h085 : spiaddr = 8'h4d; gpio_adr | 12'h084 : spiaddr = 8'h4e; gpio_adr | 12'h089 : spiaddr = 8'h4f; gpio_adr | 12'h088 : spiaddr = 8'h50; gpio_adr | 12'h08d : spiaddr = 8'h51; gpio_adr | 12'h08c : spiaddr = 8'h52; gpio_adr | 12'h091 : spiaddr = 8'h53; gpio_adr | 12'h090 : spiaddr = 8'h54; gpio_adr | 12'h095 : spiaddr = 8'h55; gpio_adr | 12'h094 : spiaddr = 8'h56; gpio_adr | 12'h099 : spiaddr = 8'h57; gpio_adr | 12'h098 : spiaddr = 8'h58; gpio_adr | 12'h09d : spiaddr = 8'h59; gpio_adr | 12'h09c : spiaddr = 8'h5a; gpio_adr | 12'h0a1 : spiaddr = 8'h5b; gpio_adr | 12'h0a0 : spiaddr = 8'h5c; gpio_adr | 12'h0a5 : spiaddr = 8'h5d; gpio_adr | 12'h0a4 : spiaddr = 8'h5e; gpio_adr | 12'h0a9 : spiaddr = 8'h5f; gpio_adr | 12'h0a8 : spiaddr = 8'h60; gpio_adr | 12'h0ad : spiaddr = 8'h61; gpio_adr | 12'h0ac : spiaddr = 8'h62; gpio_adr | 12'h0b1 : spiaddr = 8'h63; gpio_adr | 12'h0b0 : spiaddr = 8'h64; gpio_adr | 12'h0b5 : spiaddr = 8'h65; gpio_adr | 12'h0b4 : spiaddr = 8'h66; gpio_adr | 12'h0b9 : spiaddr = 8'h67; gpio_adr | 12'h0b8 : spiaddr = 8'h68; gpio_adr | 12'h010 : spiaddr = 8'h69; // GPIO data (h) gpio_adr | 12'h00f : spiaddr = 8'h6a; // GPIO data (l) gpio_adr | 12'h00e : spiaddr = 8'h6b; // GPIO data (l) gpio_adr | 12'h00d : spiaddr = 8'h6c; // GPIO data (l) gpio_adr | 12'h00c : spiaddr = 8'h6d; // GPIO data (l) gpio_adr | 12'h004 : spiaddr = 8'h6e; // Power control sys_adr | 12'h010 : spiaddr = 8'h6f; // Housekeeping SPI disable default : spiaddr = 8'h00; endcase end endfunction // SPI is considered active when the GPIO for CSB is set to input and // CSB is low. SPI is considered "busy" when rdstb or wrstb are high, // indicating that the SPI will read or write a byte on the next SCK // transition. wire spi_is_enabled = (~gpio_configure[3][INP_DIS]) & (~hkspi_disable); wire spi_is_active = spi_is_enabled && (mgmt_gpio_in[3] == 1'b0); wire spi_is_busy = spi_is_active && (rdstb || wrstb); /* Wishbone back-door state machine and address translation */ always @(posedge wb_clk_i or posedge wb_rst_i) begin if (wb_rst_i) begin wbbd_sck <= 1'b0; wbbd_write <= 1'b0; wbbd_addr <= 8'd0; wbbd_data <= 8'd0; wbbd_busy <= 1'b0; wb_ack_o <= 1'b0; wbbd_state <= `WBBD_IDLE; end else begin case (wbbd_state) `WBBD_IDLE: begin wbbd_busy <= 1'b0; if ((sys_select | gpio_select | spi_select) && wb_cyc_i && wb_stb_i) begin wb_ack_o <= 1'b0; wbbd_state <= `WBBD_SETUP0; end end `WBBD_SETUP0: begin wbbd_sck <= 1'b0; wbbd_addr <= spiaddr(wb_adr_i); if (wb_sel_i[0] & wb_we_i) begin wbbd_data <= wb_dat_i[7:0]; end wbbd_write <= wb_sel_i[0] & wb_we_i; wbbd_busy <= 1'b1; // If the SPI is being accessed and about to read or // write a byte, then stall until the SPI is ready. if (!spi_is_busy) begin wbbd_state <= `WBBD_RW0; end end `WBBD_RW0: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b1; wb_dat_o[7:0] <= odata; wbbd_state <= `WBBD_SETUP1; end `WBBD_SETUP1: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b0; wbbd_addr <= spiaddr(wb_adr_i + 1); if (wb_sel_i[1] & wb_we_i) begin wbbd_data <= wb_dat_i[15:8]; end wbbd_write <= wb_sel_i[1] & wb_we_i; if (!spi_is_busy) begin wbbd_state <= `WBBD_RW1; end end `WBBD_RW1: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b1; wb_dat_o[15:8] <= odata; wbbd_state <= `WBBD_SETUP2; end `WBBD_SETUP2: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b0; wbbd_addr <= spiaddr(wb_adr_i + 2); if (wb_sel_i[2] & wb_we_i) begin wbbd_data <= wb_dat_i[23:16]; end wbbd_write <= wb_sel_i[2] & wb_we_i; if (!spi_is_busy) begin wbbd_state <= `WBBD_RW2; end end `WBBD_RW2: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b1; wb_dat_o[23:16] <= odata; wbbd_state <= `WBBD_SETUP3; end `WBBD_SETUP3: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b0; wbbd_addr <= spiaddr(wb_adr_i + 3); if (wb_sel_i[3] & wb_we_i) begin wbbd_data <= wb_dat_i[31:24]; end wbbd_write <= wb_sel_i[3] & wb_we_i; if (!spi_is_busy) begin wbbd_state <= `WBBD_RW3; end end `WBBD_RW3: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b1; wb_dat_o[31:24] <= odata; wb_ack_o <= 1'b1; // Release hold on wishbone bus wbbd_state <= `WBBD_DONE; end `WBBD_DONE: begin wbbd_busy <= 1'b1; wbbd_sck <= 1'b0; wb_ack_o <= 1'b0; // Reset for next access wbbd_write <= 1'b0; wbbd_state <= `WBBD_IDLE; end endcase end end // Instantiate the SPI interface protocol module housekeeping_spi hkspi ( .reset(~porb), .SCK(mgmt_gpio_in[4]), .SDI(mgmt_gpio_in[2]), .CSB((spi_is_enabled) ? mgmt_gpio_in[3] : 1'b1), .SDO(sdo), .sdoenb(sdo_enb), .idata(odata), .odata(idata), .oaddr(iaddr), .rdstb(rdstb), .wrstb(wrstb), .pass_thru_mgmt(pass_thru_mgmt), .pass_thru_mgmt_delay(pass_thru_mgmt_delay), .pass_thru_user(pass_thru_user), .pass_thru_user_delay(pass_thru_user_delay), .pass_thru_mgmt_reset(pass_thru_mgmt_reset), .pass_thru_user_reset(pass_thru_user_reset) ); // GPIO data handling to and from the management SoC assign mgmt_gpio_out[37] = (qspi_enabled) ? spimemio_flash_io3_do : mgmt_gpio_data[37]; assign mgmt_gpio_out[36] = (qspi_enabled) ? spimemio_flash_io2_do : mgmt_gpio_data[36]; assign mgmt_gpio_oeb[37] = (qspi_enabled) ? spimemio_flash_io3_oeb : ~gpio_configure[37][INP_DIS]; assign mgmt_gpio_oeb[36] = (qspi_enabled) ? spimemio_flash_io2_oeb : ~gpio_configure[36][INP_DIS]; assign mgmt_gpio_oeb[35] = (spi_enabled) ? spi_sdoenb : ~gpio_configure[35][INP_DIS]; // NOTE: Ignored by spimemio module when QSPI disabled, so they do not // need any exception when qspi_enabled == 1. assign spimemio_flash_io3_di = mgmt_gpio_in[37]; assign spimemio_flash_io2_di = mgmt_gpio_in[36]; // SPI master is assigned to the other 4 bits of the data high word. assign mgmt_gpio_out[32] = (spi_enabled) ? spi_sck : mgmt_gpio_data[32]; assign mgmt_gpio_out[33] = (spi_enabled) ? spi_csb : mgmt_gpio_data[33]; assign mgmt_gpio_out[34] = mgmt_gpio_data[34]; assign mgmt_gpio_out[35] = (spi_enabled) ? spi_sdo : mgmt_gpio_data[35]; assign mgmt_gpio_out[31:16] = mgmt_gpio_data[31:16]; assign mgmt_gpio_out[12:11] = mgmt_gpio_data[12:11]; assign mgmt_gpio_out[10] = (pass_thru_user_delay) ? mgmt_gpio_in[2] : mgmt_gpio_data[10]; assign mgmt_gpio_out_9_prebuff = (pass_thru_user) ? mgmt_gpio_in[4] : mgmt_gpio_data[9]; (* keep *) sky130_fd_sc_hd__clkbuf_8 mgmt_gpio_9_buff_inst ( `ifdef USE_POWER_PINS .VPWR(VPWR), .VGND(VGND), .VPB(VPWR), .VNB(VGND), `endif .A(mgmt_gpio_out_9_prebuff), .X(mgmt_gpio_out[9])); assign mgmt_gpio_out[8] = (pass_thru_user_delay) ? mgmt_gpio_in[3] : mgmt_gpio_data[8]; assign mgmt_gpio_out[7] = mgmt_gpio_data[7]; assign mgmt_gpio_out[6] = (uart_enabled) ? ser_tx : mgmt_gpio_data[6]; assign mgmt_gpio_out[5:2] = mgmt_gpio_data[5:2]; // In pass-through modes, route SDO from the respective flash (user or // management SoC) to the dedicated SDO pin (GPIO[1]) assign mgmt_gpio_out[1] = (pass_thru_mgmt) ? pad_flash_io1_di : (pass_thru_user) ? mgmt_gpio_in[11] : (spi_is_active) ? sdo : mgmt_gpio_data[1]; assign mgmt_gpio_out[0] = (debug_mode) ? debug_out : mgmt_gpio_data[0]; assign mgmt_gpio_oeb[1] = (spi_is_active) ? sdo_enb : ~gpio_configure[1][INP_DIS]; assign mgmt_gpio_oeb[0] = (debug_mode) ? debug_oeb : ~gpio_configure[0][INP_DIS]; assign ser_rx = (uart_enabled) ? mgmt_gpio_in[5] : 1'b0; assign spi_sdi = (spi_enabled) ? mgmt_gpio_in[34] : 1'b0; assign debug_in = (debug_mode) ? mgmt_gpio_in[0] : 1'b0; genvar i; /* These are disconnected, but apply a meaningful signal anyway */ generate for (i = 2; i < `MPRJ_IO_PADS-3; i = i + 1) begin assign mgmt_gpio_oeb[i] = ~gpio_configure[i][INP_DIS]; end endgenerate // System monitoring. Multiplex the clock and trap // signals to the associated pad, and multiplex the irq signals // from the associated pad, when the redirection is enabled. Note // that the redirection is upstream of the user/managment multiplexing, // so the pad being under control of the user area takes precedence // over the system monitoring function. assign mgmt_gpio_out_15_prebuff = (clk2_output_dest == 1'b1) ? user_clock : mgmt_gpio_data[15]; (* keep *) sky130_fd_sc_hd__clkbuf_8 mgmt_gpio_15_buff_inst ( `ifdef USE_POWER_PINS .VPWR(VPWR), .VGND(VGND), .VPB(VPWR), .VNB(VGND), `endif .A(mgmt_gpio_out_15_prebuff), .X(mgmt_gpio_out[15])); assign mgmt_gpio_out_14_prebuff = (clk1_output_dest == 1'b1) ? wb_clk_i : mgmt_gpio_data[14]; (* keep *) sky130_fd_sc_hd__clkbuf_8 mgmt_gpio_14_buff_inst ( `ifdef USE_POWER_PINS .VPWR(VPWR), .VGND(VGND), .VPB(VPWR), .VNB(VGND), `endif .A(mgmt_gpio_out_14_prebuff), .X(mgmt_gpio_out[14])); assign mgmt_gpio_out[13] = (trap_output_dest == 1'b1) ? trap : mgmt_gpio_data[13]; assign irq[0] = irq_spi; assign irq[1] = (irq_1_inputsrc == 1'b1) ? mgmt_gpio_in[7] : 1'b0; assign irq[2] = (irq_2_inputsrc == 1'b1) ? mgmt_gpio_in[12] : 1'b0; // GPIO serial loader and GPIO management control `define GPIO_IDLE 2'b00 `define GPIO_START 2'b01 `define GPIO_XBYTE 2'b10 `define GPIO_LOAD 2'b11 reg [3:0] xfer_count; reg [4:0] pad_count_1; reg [5:0] pad_count_2; reg [1:0] xfer_state; reg serial_clock_pre; reg serial_resetn_pre; reg serial_load_pre; wire serial_data_1; wire serial_data_2; wire serial_clock; wire serial_resetn; wire serial_load; reg [IO_CTRL_BITS-1:0] serial_data_staging_1; reg [IO_CTRL_BITS-1:0] serial_data_staging_2; assign serial_clock = (serial_bb_enable == 1'b1) ? serial_bb_clock : serial_clock_pre; assign serial_resetn = (serial_bb_enable == 1'b1) ? serial_bb_resetn : serial_resetn_pre; assign serial_load = (serial_bb_enable == 1'b1) ? serial_bb_load : serial_load_pre; assign serial_data_1 = (serial_bb_enable == 1'b1) ? serial_bb_data_1 : serial_data_staging_1[IO_CTRL_BITS-1]; assign serial_data_2 = (serial_bb_enable == 1'b1) ? serial_bb_data_2 : serial_data_staging_2[IO_CTRL_BITS-1]; always @(posedge wb_clk_i or negedge porb) begin if (porb == 1'b0) begin xfer_state <= `GPIO_IDLE; xfer_count <= 4'd0; /* NOTE: This assumes that MPRJ_IO_PADS_1 and MPRJ_IO_PADS_2 are * equal, because they get clocked the same number of cycles by * the same clock signal. pad_count_2 gates the count for both. */ pad_count_1 <= `MPRJ_IO_PADS_1 - 1; pad_count_2 <= `MPRJ_IO_PADS_1; serial_resetn_pre <= 1'b0; serial_clock_pre <= 1'b0; serial_load_pre <= 1'b0; serial_data_staging_1 <= 0; serial_data_staging_2 <= 0; serial_busy <= 1'b0; end else begin serial_resetn_pre <= 1'b1; case (xfer_state) `GPIO_IDLE: begin pad_count_1 <= `MPRJ_IO_PADS_1 - 1; pad_count_2 <= `MPRJ_IO_PADS_1; serial_clock_pre <= 1'b0; serial_load_pre <= 1'b0; if (serial_xfer == 1'b1) begin xfer_state <= `GPIO_START; serial_busy <= 1'b1; end else begin serial_busy <= 1'b0; end end `GPIO_START: begin serial_clock_pre <= 1'b0; serial_load_pre <= 1'b0; xfer_count <= 6'd0; pad_count_1 <= pad_count_1 - 1; pad_count_2 <= pad_count_2 + 1; xfer_state <= `GPIO_XBYTE; serial_data_staging_1 <= gpio_configure[pad_count_1]; serial_data_staging_2 <= gpio_configure[pad_count_2]; end `GPIO_XBYTE: begin serial_clock_pre <= ~serial_clock; serial_load_pre <= 1'b0; if (serial_clock == 1'b0) begin if (xfer_count == IO_CTRL_BITS - 1) begin xfer_count <= 4'd0; if (pad_count_2 == `MPRJ_IO_PADS) begin xfer_state <= `GPIO_LOAD; end else begin xfer_state <= `GPIO_START; end end else begin xfer_count <= xfer_count + 1; end end else begin serial_data_staging_1 <= {serial_data_staging_1[IO_CTRL_BITS-2:0], 1'b0}; serial_data_staging_2 <= {serial_data_staging_2[IO_CTRL_BITS-2:0], 1'b0}; end end `GPIO_LOAD: begin xfer_count <= xfer_count + 1; /* Load sequence: Pulse clock for final data shift in; * Pulse the load strobe. * Return to idle mode. */ if (xfer_count == 4'd0) begin serial_clock_pre <= 1'b0; serial_load_pre <= 1'b0; end else if (xfer_count == 4'd1) begin serial_clock_pre <= 1'b0; serial_load_pre <= 1'b1; end else if (xfer_count == 4'd2) begin serial_busy <= 1'b0; serial_clock_pre <= 1'b0; serial_load_pre <= 1'b0; xfer_state <= `GPIO_IDLE; end end endcase end end // SPI Identification assign mfgr_id = 12'h456; // Hard-coded assign prod_id = 8'h11; // Hard-coded assign mask_rev = mask_rev_in; // Copy in to out. // SPI Data transfer protocol. The wishbone back door may only be // used if the front door is closed (CSB is high or the CSB pin is // not an input). The time to apply values for the back door access // is limited to the clock cycle around the read or write from the // wbbd state machine (see below). assign caddr = (wbbd_busy) ? wbbd_addr : iaddr; assign csclk = (wbbd_busy) ? wbbd_sck : ((spi_is_active) ? mgmt_gpio_in[4] : 1'b0); assign cdata = (wbbd_busy) ? wbbd_data : idata; assign cwstb = (wbbd_busy) ? wbbd_write : wrstb; assign odata = fdata(caddr); // Register mapping and I/O to SPI interface module integer j; always @(posedge csclk or negedge porb) begin if (porb == 1'b0) begin // Set trim for PLL at (almost) slowest rate (~90MHz). However, // pll_trim[12] must be set to zero for proper startup. pll_trim <= 26'b11111111111110111111111111; pll_sel <= 3'b010; // Default output divider divide-by-2 pll90_sel <= 3'b010; // Default secondary output divider divide-by-2 pll_div <= 5'b00100; // Default feedback divider divide-by-8 pll_dco_ena <= 1'b1; // Default free-running PLL pll_ena <= 1'b0; // Default PLL turned off pll_bypass <= 1'b1; // Default bypass mode (don't use PLL) irq_spi <= 1'b0; reset_reg <= 1'b0; // System monitoring signals clk1_output_dest <= 1'b0; clk2_output_dest <= 1'b0; trap_output_dest <= 1'b0; irq_1_inputsrc <= 1'b0; irq_2_inputsrc <= 1'b0; // GPIO Configuration, Data, and Control // To-do: Get user project pad defaults from external inputs // to be configured by user or at project generation time. // Pads 1 to 4 are the SPI and considered critical startup // infrastructure, and should not be altered from the defaults // below. NOTE: These are not startup values, but they should // match the startup values applied to the GPIO, or else the // GPIO should be always triggered to load at startup. for (j = 0; j < `MPRJ_IO_PADS; j=j+1) begin if ((j < 2) || (j >= `MPRJ_IO_PADS - 2)) begin gpio_configure[j] <= 'h1803; end else begin if (j == 3) begin // j == 3 corresponds to CSB, which is a weak pull-up gpio_configure[j] <= 'h0801; end else begin gpio_configure[j] <= 'h0403; end end end mgmt_gpio_data <= 'd0; mgmt_gpio_data_buf <= 'd0; serial_bb_enable <= 1'b0; serial_bb_load <= 1'b0; serial_bb_data_1 <= 1'b0; serial_bb_data_2 <= 1'b0; serial_bb_clock <= 1'b0; serial_bb_resetn <= 1'b0; serial_xfer <= 1'b0; hkspi_disable <= 1'b0; pwr_ctrl_out <= 'd0; `ifdef USE_SRAM_RO_INTERFACE sram_ro_clk <= 1'b0; sram_ro_csb <= 1'b1; sram_ro_addr <= 8'h00; `endif end else begin if (cwstb == 1'b1) begin case (caddr) /* Register 8'h00 is reserved for future use */ /* Registers 8'h01 to 8'h07 are read-only and cannot be written */ 8'h08: begin pll_ena <= cdata[0]; pll_dco_ena <= cdata[1]; end 8'h09: begin pll_bypass <= cdata[0]; end 8'h0a: begin irq_spi <= cdata[0]; end 8'h0b: begin reset_reg <= cdata[0]; end /* Register 0c (trap state) is read-only */ 8'h0d: begin pll_trim[7:0] <= cdata; end 8'h0e: begin pll_trim[15:8] <= cdata; end 8'h0f: begin pll_trim[23:16] <= cdata; end 8'h10: begin pll_trim[25:24] <= cdata[1:0]; end 8'h11: begin pll90_sel <= cdata[5:3]; pll_sel <= cdata[2:0]; end 8'h12: begin pll_div <= cdata[4:0]; end 8'h13: begin serial_bb_data_2 <= cdata[6]; serial_bb_data_1 <= cdata[5]; serial_bb_clock <= cdata[4]; serial_bb_load <= cdata[3]; serial_bb_resetn <= cdata[2]; serial_bb_enable <= cdata[1]; serial_xfer <= cdata[0]; end `ifdef USE_SRAM_RO_INTERFACE /* Optional: Add SRAM read-only interface */ 8'h14: begin sram_ro_clk <= cdata[1]; sram_ro_csb <= cdata[0]; end 8'h15: begin sram_ro_addr <= cdata; end `endif /* Registers 16 to 19 (SRAM data) are read-only */ /* Register 1a (power monitor) is read-only */ 8'h1b: begin clk1_output_dest <= cdata[2]; clk2_output_dest <= cdata[1]; trap_output_dest <= cdata[0]; end 8'h1c: begin irq_2_inputsrc <= cdata[1]; irq_1_inputsrc <= cdata[0]; end 8'h1d: begin gpio_configure[0][12:8] <= cdata[4:0]; end 8'h1e: begin gpio_configure[0][7:0] <= cdata; end 8'h1f: begin gpio_configure[1][12:8] <= cdata[4:0]; end 8'h20: begin gpio_configure[1][7:0] <= cdata; end 8'h21: begin gpio_configure[2][12:8] <= cdata[4:0]; end 8'h22: begin gpio_configure[2][7:0] <= cdata; end 8'h23: begin gpio_configure[3][12:8] <= cdata[4:0]; end 8'h24: begin gpio_configure[3][7:0] <= cdata; end 8'h25: begin gpio_configure[4][12:8] <= cdata[4:0]; end 8'h26: begin gpio_configure[4][7:0] <= cdata; end 8'h27: begin gpio_configure[5][12:8] <= cdata[4:0]; end 8'h28: begin gpio_configure[5][7:0] <= cdata; end 8'h29: begin gpio_configure[6][12:8] <= cdata[4:0]; end 8'h2a: begin gpio_configure[6][7:0] <= cdata; end 8'h2b: begin gpio_configure[7][12:8] <= cdata[4:0]; end 8'h2c: begin gpio_configure[7][7:0] <= cdata; end 8'h2d: begin gpio_configure[8][12:8] <= cdata[4:0]; end 8'h2e: begin gpio_configure[8][7:0] <= cdata; end 8'h2f: begin gpio_configure[9][12:8] <= cdata[4:0]; end 8'h30: begin gpio_configure[9][7:0] <= cdata; end 8'h31: begin gpio_configure[10][12:8] <= cdata[4:0]; end 8'h32: begin gpio_configure[10][7:0] <= cdata; end 8'h33: begin gpio_configure[11][12:8] <= cdata[4:0]; end 8'h34: begin gpio_configure[11][7:0] <= cdata; end 8'h35: begin gpio_configure[12][12:8] <= cdata[4:0]; end 8'h36: begin gpio_configure[12][7:0] <= cdata; end 8'h37: begin gpio_configure[13][12:8] <= cdata[4:0]; end 8'h38: begin gpio_configure[13][7:0] <= cdata; end 8'h39: begin gpio_configure[14][12:8] <= cdata[4:0]; end 8'h3a: begin gpio_configure[14][7:0] <= cdata; end 8'h3b: begin gpio_configure[15][12:8] <= cdata[4:0]; end 8'h3c: begin gpio_configure[15][7:0] <= cdata; end 8'h3d: begin gpio_configure[16][12:8] <= cdata[4:0]; end 8'h3e: begin gpio_configure[16][7:0] <= cdata; end 8'h3f: begin gpio_configure[17][12:8] <= cdata[4:0]; end 8'h40: begin gpio_configure[17][7:0] <= cdata; end 8'h41: begin gpio_configure[18][12:8] <= cdata[4:0]; end 8'h42: begin gpio_configure[18][7:0] <= cdata; end 8'h43: begin gpio_configure[19][12:8] <= cdata[4:0]; end 8'h44: begin gpio_configure[19][7:0] <= cdata; end 8'h45: begin gpio_configure[20][12:8] <= cdata[4:0]; end 8'h46: begin gpio_configure[20][7:0] <= cdata; end 8'h47: begin gpio_configure[21][12:8] <= cdata[4:0]; end 8'h48: begin gpio_configure[21][7:0] <= cdata; end 8'h49: begin gpio_configure[22][12:8] <= cdata[4:0]; end 8'h4a: begin gpio_configure[22][7:0] <= cdata; end 8'h4b: begin gpio_configure[23][12:8] <= cdata[4:0]; end 8'h4c: begin gpio_configure[23][7:0] <= cdata; end 8'h4d: begin gpio_configure[24][12:8] <= cdata[4:0]; end 8'h4e: begin gpio_configure[24][7:0] <= cdata; end 8'h4f: begin gpio_configure[25][12:8] <= cdata[4:0]; end 8'h50: begin gpio_configure[25][7:0] <= cdata; end 8'h51: begin gpio_configure[26][12:8] <= cdata[4:0]; end 8'h52: begin gpio_configure[26][7:0] <= cdata; end 8'h53: begin gpio_configure[27][12:8] <= cdata[4:0]; end 8'h54: begin gpio_configure[27][7:0] <= cdata; end 8'h55: begin gpio_configure[28][12:8] <= cdata[4:0]; end 8'h56: begin gpio_configure[28][7:0] <= cdata; end 8'h57: begin gpio_configure[29][12:8] <= cdata[4:0]; end 8'h58: begin gpio_configure[29][7:0] <= cdata; end 8'h59: begin gpio_configure[30][12:8] <= cdata[4:0]; end 8'h5a: begin gpio_configure[30][7:0] <= cdata; end 8'h5b: begin gpio_configure[31][12:8] <= cdata[4:0]; end 8'h5c: begin gpio_configure[31][7:0] <= cdata; end 8'h5d: begin gpio_configure[32][12:8] <= cdata[4:0]; end 8'h5e: begin gpio_configure[32][7:0] <= cdata; end 8'h5f: begin gpio_configure[33][12:8] <= cdata[4:0]; end 8'h60: begin gpio_configure[33][7:0] <= cdata; end 8'h61: begin gpio_configure[34][12:8] <= cdata[4:0]; end 8'h62: begin gpio_configure[34][7:0] <= cdata; end 8'h63: begin gpio_configure[35][12:8] <= cdata[4:0]; end 8'h64: begin gpio_configure[35][7:0] <= cdata; end 8'h65: begin gpio_configure[36][12:8] <= cdata[4:0]; end 8'h66: begin gpio_configure[36][7:0] <= cdata; end 8'h67: begin gpio_configure[37][12:8] <= cdata[4:0]; end 8'h68: begin gpio_configure[37][7:0] <= cdata; end 8'h69: begin mgmt_gpio_data[37:32] <= cdata[5:0]; end 8'h6a: begin /* NOTE: mgmt_gpio_data updates only on the */ /* upper byte write when writing through the */ /* wishbone back-door. This lets all bits */ /* update at the same time. */ if (spi_is_active) begin mgmt_gpio_data[31:24] <= cdata; end else begin mgmt_gpio_data[31:0] <= {cdata, mgmt_gpio_data_buf}; end end 8'h6b: begin if (spi_is_active) begin mgmt_gpio_data[23:16] <= cdata; end else begin mgmt_gpio_data_buf[23:16] <= cdata; end end 8'h6c: begin if (spi_is_active) begin mgmt_gpio_data[15:8] <= cdata; end else begin mgmt_gpio_data_buf[15:8] <= cdata; end end 8'h6d: begin if (spi_is_active) begin mgmt_gpio_data[7:0] <= cdata; end else begin mgmt_gpio_data_buf[7:0] <= cdata; end end 8'h6e: begin pwr_ctrl_out <= cdata[3:0]; end 8'h6f: begin hkspi_disable <= cdata[0]; end endcase // (caddr) end else begin serial_xfer <= 1'b0; // Serial transfer is self-resetting irq_spi <= 1'b0; // IRQ is self-resetting end end end endmodule // housekeeping `default_nettype wire