mirror of https://github.com/efabless/caravel.git
1396 lines
49 KiB
Verilog
1396 lines
49 KiB
Verilog
// 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 vdd,
|
|
inout vss,
|
|
`endif
|
|
|
|
// Wishbone interface to management SoC
|
|
input wb_clk_i,
|
|
input wb_rst_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_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,
|
|
|
|
output sram_ro_clk,
|
|
output sram_ro_csb,
|
|
output [7:0] sram_ro_addr,
|
|
input [31:0] sram_ro_data,
|
|
|
|
// 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_resetn;
|
|
reg serial_bb_data_1;
|
|
reg serial_bb_data_2;
|
|
reg serial_bb_enable;
|
|
reg serial_xfer;
|
|
reg hkspi_disable;
|
|
|
|
reg sram_ro_clk;
|
|
reg sram_ro_csb;
|
|
reg [7:0] sram_ro_addr;
|
|
|
|
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
|
|
|
|
wire [31:0] sram_ro_data;
|
|
|
|
// Housekeeping side 3-wire interface to GPIOs (see below)
|
|
wire [`MPRJ_IO_PADS-1:0] mgmt_gpio_out_pre;
|
|
|
|
// 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;
|
|
|
|
// Handle the management-side control of the GPIO pins. All but the
|
|
// first and last three GPIOs (0, 1 and 35 to 37) are one-pin interfaces with
|
|
// a single I/O pin whose direction is determined by the local OEB signal.
|
|
// The other five are straight-through connections of the 3-wire interface.
|
|
|
|
assign mgmt_gpio_out[`MPRJ_IO_PADS-1:`MPRJ_IO_PADS-3] =
|
|
mgmt_gpio_out_pre[`MPRJ_IO_PADS-1:`MPRJ_IO_PADS-3];
|
|
assign mgmt_gpio_out[1:0] = mgmt_gpio_out_pre[1:0];
|
|
|
|
genvar i;
|
|
|
|
// This implements high-impedence buffers on the GPIO outputs other than
|
|
// the first and last two GPIOs so that these pins can be tied together
|
|
// at the top level to create the single-wire interface on those GPIOs.
|
|
generate
|
|
for (i = 2; i < `MPRJ_IO_PADS-3; i = i + 1) begin
|
|
assign mgmt_gpio_out[i] = mgmt_gpio_oeb[i] ? 1'bz : mgmt_gpio_out_pre[i];
|
|
end
|
|
endgenerate
|
|
|
|
// 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 = (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;
|
|
|
|
// 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 = {2'b00, serial_data_2, serial_data_1, serial_bb_clock,
|
|
serial_bb_resetn, serial_bb_enable, serial_busy};
|
|
|
|
/* To be added: 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];
|
|
|
|
/* 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
|
|
|
|
/* To be added: SRAM read-only interface */
|
|
|
|
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
|
|
|
|
/* 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 (wb_cyc_i && (sys_select | gpio_select | spi_select)) 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_active) ? 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)
|
|
);
|
|
|
|
// 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);
|
|
|
|
// GPIO data handling to and from the management SoC
|
|
|
|
assign mgmt_gpio_out_pre[37] = (qspi_enabled) ? spimemio_flash_io3_do :
|
|
mgmt_gpio_data[37];
|
|
assign mgmt_gpio_out_pre[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_pre[32] = (spi_enabled) ? spi_sck : mgmt_gpio_data[32];
|
|
assign mgmt_gpio_out_pre[33] = (spi_enabled) ? spi_csb : mgmt_gpio_data[33];
|
|
assign mgmt_gpio_out_pre[34] = mgmt_gpio_data[34];
|
|
assign mgmt_gpio_out_pre[35] = (spi_enabled) ? spi_sdo : mgmt_gpio_data[35];
|
|
|
|
assign mgmt_gpio_out_pre[31:16] = mgmt_gpio_data[31:16];
|
|
assign mgmt_gpio_out_pre[12:11] = mgmt_gpio_data[12:11];
|
|
|
|
assign mgmt_gpio_out_pre[10] = (pass_thru_user) ? mgmt_gpio_in[2]
|
|
: mgmt_gpio_data[10];
|
|
assign mgmt_gpio_out_pre[9] = (pass_thru_user) ? mgmt_gpio_in[4]
|
|
: mgmt_gpio_data[9];
|
|
assign mgmt_gpio_out_pre[8] = (pass_thru_user_delay) ? mgmt_gpio_in[3]
|
|
: mgmt_gpio_data[8];
|
|
|
|
assign mgmt_gpio_out_pre[7] = mgmt_gpio_data[7];
|
|
assign mgmt_gpio_out_pre[6] = (uart_enabled) ? ser_tx : mgmt_gpio_data[6];
|
|
assign mgmt_gpio_out_pre[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_pre[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_pre[0] = (debug_mode) ? debug_out : mgmt_gpio_data[0];
|
|
|
|
assign mgmt_gpio_oeb[1] = (spi_is_active) ? sdo_enb : ~gpio_configure[0][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;
|
|
|
|
/* 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_pre[15] = (clk2_output_dest == 1'b1) ? user_clock
|
|
: mgmt_gpio_data[15];
|
|
assign mgmt_gpio_out_pre[14] = (clk1_output_dest == 1'b1) ? wb_clk_i
|
|
: mgmt_gpio_data[14];
|
|
assign mgmt_gpio_out_pre[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_busy;
|
|
wire serial_data_1;
|
|
wire serial_data_2;
|
|
wire serial_clock;
|
|
wire serial_resetn;
|
|
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_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_data_staging_1 <= 0;
|
|
serial_data_staging_2 <= 0;
|
|
serial_busy <= 1'b0;
|
|
|
|
end else begin
|
|
|
|
case (xfer_state)
|
|
`GPIO_IDLE: begin
|
|
pad_count_1 <= `MPRJ_IO_PADS_1 - 1;
|
|
pad_count_2 <= `MPRJ_IO_PADS_1;
|
|
serial_resetn_pre <= 1'b1;
|
|
serial_clock_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_resetn_pre <= 1'b1;
|
|
serial_clock_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_resetn_pre <= 1'b1;
|
|
serial_clock_pre <= ~serial_clock;
|
|
if (serial_clock == 1'b0) begin
|
|
if (xfer_count == IO_CTRL_BITS - 1) begin
|
|
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: Raise clock for final data shift in;
|
|
* Pulse reset low while clock is high
|
|
* Set clock back to zero.
|
|
* Return to idle mode.
|
|
*/
|
|
if (xfer_count == 4'd0) begin
|
|
serial_clock_pre <= 1'b1;
|
|
serial_resetn_pre <= 1'b0;
|
|
end else if (xfer_count == 4'd1) begin
|
|
serial_clock_pre <= 1'b1;
|
|
serial_resetn_pre <= 1'b1;
|
|
end else if (xfer_count == 4'd2) begin
|
|
serial_busy <= 1'b0;
|
|
serial_resetn_pre <= 1'b1;
|
|
serial_clock_pre <= 1'b0;
|
|
xfer_state <= `GPIO_IDLE;
|
|
end
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
|
|
// SPI Identification
|
|
|
|
wire [11:0] mfgr_id;
|
|
wire [7:0] prod_id;
|
|
wire [31:0] mask_rev;
|
|
|
|
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
|
|
gpio_configure[j] <= 'h0403;
|
|
end
|
|
end
|
|
|
|
mgmt_gpio_data <= 'd0;
|
|
mgmt_gpio_data_buf <= 'd0;
|
|
serial_bb_enable <= 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;
|
|
|
|
sram_ro_clk <= 1'b0;
|
|
sram_ro_csb <= 1'b1;
|
|
sram_ro_addr <= 8'h00;
|
|
|
|
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[5];
|
|
serial_bb_data_1 <= cdata[4];
|
|
serial_bb_clock <= cdata[3];
|
|
serial_bb_resetn <= cdata[2];
|
|
serial_bb_enable <= cdata[1];
|
|
serial_xfer <= cdata[0];
|
|
end
|
|
|
|
/* To be done: 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
|
|
|
|
/* 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
|