| // 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 |
| module mprj_ctrl_wb #( |
| parameter BASE_ADR = 32'h 2300_0000, |
| parameter XFER = 8'h 00, |
| parameter PWRDATA = 8'h 04, |
| parameter IRQDATA = 8'h 08, |
| parameter IODATA = 8'h 0c, // One word per 32 IOs |
| parameter IOCONFIG = 8'h 24 |
| )( |
| input wb_clk_i, |
| input wb_rst_i, |
| |
| input [31:0] wb_dat_i, |
| input [31:0] wb_adr_i, |
| input [3:0] wb_sel_i, |
| input wb_cyc_i, |
| input wb_stb_i, |
| input wb_we_i, |
| |
| output [31:0] wb_dat_o, |
| output wb_ack_o, |
| |
| // Output is to serial loader |
| output serial_clock, |
| output serial_resetn, |
| output serial_data_out_1, |
| output serial_data_out_2, |
| |
| // Pass state of OEB bit on SDO and JTAG back to the core |
| // so that the function can be overridden for management output |
| output sdo_oenb_state, |
| output jtag_oenb_state, |
| output flash_io2_oenb_state, |
| output flash_io3_oenb_state, |
| |
| // Read/write data to each GPIO pad from management SoC |
| input [`MPRJ_IO_PADS-1:0] mgmt_gpio_in, |
| output [`MPRJ_IO_PADS-1:0] mgmt_gpio_out, |
| |
| // Write data to power controls |
| output [`MPRJ_PWR_PADS-1:0] pwr_ctrl_out, |
| |
| // Enable user project IRQ signals to management SoC |
| output [2:0] user_irq_ena |
| ); |
| wire resetn; |
| wire valid; |
| wire ready; |
| wire [3:0] iomem_we; |
| |
| assign resetn = ~wb_rst_i; |
| assign valid = wb_stb_i && wb_cyc_i; |
| |
| assign iomem_we = wb_sel_i & {4{wb_we_i}}; |
| assign wb_ack_o = ready; |
| |
| mprj_ctrl #( |
| .BASE_ADR(BASE_ADR), |
| .XFER(XFER), |
| .PWRDATA(PWRDATA), |
| .IRQDATA(IRQDATA), |
| .IODATA(IODATA), |
| .IOCONFIG(IOCONFIG) |
| ) mprj_ctrl ( |
| .clk(wb_clk_i), |
| .resetn(resetn), |
| .iomem_addr(wb_adr_i), |
| .iomem_valid(valid), |
| .iomem_wstrb(iomem_we[1:0]), |
| .iomem_wdata(wb_dat_i), |
| .iomem_rdata(wb_dat_o), |
| .iomem_ready(ready), |
| |
| .serial_clock(serial_clock), |
| .serial_resetn(serial_resetn), |
| .serial_data_out_1(serial_data_out_1), |
| .serial_data_out_2(serial_data_out_2), |
| .sdo_oenb_state(sdo_oenb_state), |
| .jtag_oenb_state(jtag_oenb_state), |
| .flash_io2_oenb_state(flash_io2_oenb_state), |
| .flash_io3_oenb_state(flash_io3_oenb_state), |
| // .mgmt_gpio_io(mgmt_gpio_io) |
| .mgmt_gpio_in(mgmt_gpio_in), |
| .mgmt_gpio_out(mgmt_gpio_out), |
| |
| // Write data to power controls |
| .pwr_ctrl_out(pwr_ctrl_out), |
| // Enable user project IRQ signals to management SoC |
| .user_irq_ena(user_irq_ena) |
| ); |
| |
| endmodule |
| |
| module mprj_ctrl #( |
| parameter BASE_ADR = 32'h 2300_0000, |
| parameter XFER = 8'h 00, |
| parameter PWRDATA = 8'h 04, |
| parameter IRQDATA = 8'h 08, |
| parameter IODATA = 8'h 0c, |
| parameter IOCONFIG = 8'h 24, |
| parameter IO_CTRL_BITS = 13 |
| )( |
| input clk, |
| input resetn, |
| |
| input [31:0] iomem_addr, |
| input iomem_valid, |
| input [1:0] iomem_wstrb, |
| input [31:0] iomem_wdata, |
| output reg [31:0] iomem_rdata, |
| output reg iomem_ready, |
| |
| output serial_clock, |
| output serial_resetn, |
| output serial_data_out_1, |
| output serial_data_out_2, |
| output sdo_oenb_state, |
| output jtag_oenb_state, |
| output flash_io2_oenb_state, |
| output flash_io3_oenb_state, |
| input [`MPRJ_IO_PADS-1:0] mgmt_gpio_in, |
| output [`MPRJ_IO_PADS-1:0] mgmt_gpio_out, |
| output [`MPRJ_PWR_PADS-1:0] pwr_ctrl_out, |
| output [2:0] user_irq_ena |
| ); |
| |
| `define IDLE 2'b00 |
| `define START 2'b01 |
| `define XBYTE 2'b10 |
| `define LOAD 2'b11 |
| |
| localparam IO_WORDS = (`MPRJ_IO_PADS % 32 != 0) + (`MPRJ_IO_PADS / 32); |
| |
| localparam IO_BASE_ADR = (BASE_ADR | IOCONFIG); |
| |
| localparam OEB = 1; // Offset of output enable in shift register. |
| localparam INP_DIS = 3; // Offset of input disable in shift register. |
| |
| reg [IO_CTRL_BITS-1:0] io_ctrl[`MPRJ_IO_PADS-1:0]; // I/O control, 1 word per gpio pad |
| reg [`MPRJ_IO_PADS-1:0] mgmt_gpio_outr; // I/O write data, 1 bit per gpio pad |
| wire [`MPRJ_IO_PADS-1:0] mgmt_gpio_out; // I/O write data output when input disabled |
| reg [`MPRJ_PWR_PADS-1:0] pwr_ctrl_out; // Power write data, 1 bit per power pad |
| reg [2:0] user_irq_ena; // Enable user to raise IRQs |
| reg xfer_ctrl; // Transfer control (1 bit) |
| |
| wire [IO_WORDS-1:0] io_data_sel; // wishbone selects |
| wire pwr_data_sel; |
| wire irq_data_sel; |
| wire xfer_sel; |
| wire busy; |
| wire selected; |
| wire [`MPRJ_IO_PADS-1:0] io_ctrl_sel; |
| reg [31:0] iomem_rdata_pre; |
| |
| wire [`MPRJ_IO_PADS-1:0] mgmt_gpio_in; |
| |
| wire sdo_oenb_state, jtag_oenb_state; |
| wire flash_io2_oenb_state, flash_io3_oenb_state; |
| |
| // JTAG and housekeeping SDO are normally controlled by their respective |
| // modules with OEB set to the default 1 value. If configured for an |
| // additional output by setting the OEB bit low, then pass this information |
| // back to the core so that the default signals can be overridden. |
| |
| assign jtag_oenb_state = io_ctrl[0][OEB]; |
| assign sdo_oenb_state = io_ctrl[1][OEB]; |
| |
| // Likewise for the flash_io2 and flash_io3, although they are configured |
| // as input by default. |
| assign flash_io2_oenb_state = io_ctrl[(`MPRJ_IO_PADS)-2][OEB]; |
| assign flash_io3_oenb_state = io_ctrl[(`MPRJ_IO_PADS)-1][OEB]; |
| |
| `define wtop (((i+1)*32 > `MPRJ_IO_PADS) ? `MPRJ_IO_PADS-1 : (i+1)*32-1) |
| `define wbot (i*32) |
| `define rtop (`wtop - `wbot) |
| |
| genvar i; |
| |
| // Assign selection bits per address |
| |
| assign xfer_sel = (iomem_addr[7:0] == XFER); |
| assign pwr_data_sel = (iomem_addr[7:0] == PWRDATA); |
| assign irq_data_sel = (iomem_addr[7:0] == IRQDATA); |
| |
| generate |
| for (i=0; i<IO_WORDS; i=i+1) begin |
| assign io_data_sel[i] = (iomem_addr[7:0] == (IODATA + i*4)); |
| end |
| |
| for (i=0; i<`MPRJ_IO_PADS; i=i+1) begin |
| assign io_ctrl_sel[i] = (iomem_addr[7:0] == (IO_BASE_ADR[7:0] + i*4)); |
| assign mgmt_gpio_out[i] = (io_ctrl[i][INP_DIS] == 1'b1) ? |
| mgmt_gpio_outr[i] : 1'bz; |
| end |
| endgenerate |
| |
| // Set selection and iomem_rdata_pre |
| |
| assign selected = xfer_sel || pwr_data_sel || irq_data_sel || (|io_data_sel) || (|io_ctrl_sel); |
| |
| wire [31:0] io_data_arr[0:IO_WORDS-1]; |
| wire [31:0] io_ctrl_arr[0:`MPRJ_IO_PADS-1]; |
| generate |
| for (i=0; i<IO_WORDS; i=i+1) begin |
| assign io_data_arr[i] = {{(31-`rtop){1'b0}}, mgmt_gpio_in[`wtop:`wbot]}; |
| |
| end |
| for (i=0; i<`MPRJ_IO_PADS; i=i+1) begin |
| assign io_ctrl_arr[i] = {{(32-IO_CTRL_BITS){1'b0}}, io_ctrl[i]}; |
| end |
| endgenerate |
| |
| |
| integer j; |
| always @ * begin |
| iomem_rdata_pre = 'b0; |
| if (xfer_sel) begin |
| iomem_rdata_pre = {31'b0, busy}; |
| end else if (pwr_data_sel) begin |
| iomem_rdata_pre = {{(32-`MPRJ_PWR_PADS){1'b0}}, pwr_ctrl_out}; |
| end else if (irq_data_sel) begin |
| iomem_rdata_pre = {29'b0, user_irq_ena}; |
| end else if (|io_data_sel) begin |
| for (j=0; j<IO_WORDS; j=j+1) begin |
| if (io_data_sel[j]) begin |
| iomem_rdata_pre = io_data_arr[j]; |
| end |
| end |
| end else begin |
| for (j=0; j<`MPRJ_IO_PADS; j=j+1) begin |
| if (io_ctrl_sel[j]) begin |
| iomem_rdata_pre = io_ctrl_arr[j]; |
| end |
| end |
| end |
| end |
| |
| // General I/O transfer |
| |
| always @(posedge clk) begin |
| if (!resetn) begin |
| iomem_rdata <= 0; |
| iomem_ready <= 0; |
| end else begin |
| iomem_ready <= 0; |
| if (iomem_valid && !iomem_ready && iomem_addr[31:8] == BASE_ADR[31:8]) begin |
| iomem_ready <= 1'b 1; |
| |
| if (selected) begin |
| iomem_rdata <= iomem_rdata_pre; |
| end |
| end |
| end |
| end |
| |
| // I/O write of xfer bit. Also handles iomem_ready signal and power data. |
| |
| always @(posedge clk) begin |
| if (!resetn) begin |
| xfer_ctrl <= 0; |
| pwr_ctrl_out <= 0; |
| user_irq_ena <= 0; |
| end else begin |
| if (iomem_valid && !iomem_ready && iomem_addr[31:8] == BASE_ADR[31:8]) begin |
| if (xfer_sel) begin |
| if (iomem_wstrb[0]) xfer_ctrl <= iomem_wdata[0]; |
| end else if (pwr_data_sel) begin |
| if (iomem_wstrb[0]) pwr_ctrl_out <= iomem_wdata[`MPRJ_PWR_PADS-1:0]; |
| end else if (irq_data_sel) begin |
| if (iomem_wstrb[0]) user_irq_ena <= iomem_wdata[2:0]; |
| end |
| end else begin |
| xfer_ctrl <= 1'b0; // Immediately self-resetting |
| end |
| end |
| end |
| |
| // I/O transfer of gpio data to/from user project region under management |
| // SoC control |
| |
| generate |
| for (i=0; i<IO_WORDS; i=i+1) begin |
| always @(posedge clk) begin |
| if (!resetn) begin |
| mgmt_gpio_outr[`wtop:`wbot] <= 'd0; |
| end else begin |
| if (iomem_valid && !iomem_ready && iomem_addr[31:8] == |
| BASE_ADR[31:8]) begin |
| if (io_data_sel[i]) begin |
| if (iomem_wstrb[0]) begin |
| mgmt_gpio_outr[`wtop:`wbot] <= iomem_wdata[`rtop:0]; |
| end |
| end |
| end |
| end |
| end |
| end |
| |
| for (i=0; i<`MPRJ_IO_PADS; i=i+1) begin |
| always @(posedge clk) begin |
| if (!resetn) begin |
| // NOTE: This initialization must match the defaults passed |
| // to the control blocks. Specifically, 0x1803 is for a |
| // bidirectional pad, and 0x0403 is for a simple input pad |
| if ((i < 2) || (i >= `MPRJ_IO_PADS - 2)) begin |
| io_ctrl[i] <= 'h1803; |
| end else begin |
| io_ctrl[i] <= 'h0403; |
| end |
| end else begin |
| if (iomem_valid && !iomem_ready && |
| iomem_addr[31:8] == BASE_ADR[31:8]) begin |
| if (io_ctrl_sel[i]) begin |
| // NOTE: Byte-wide write to io_ctrl is prohibited |
| if (iomem_wstrb[0]) |
| io_ctrl[i] <= iomem_wdata[IO_CTRL_BITS-1:0]; |
| end |
| end |
| end |
| end |
| end |
| endgenerate |
| |
| 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; |
| reg serial_resetn; |
| |
| reg [IO_CTRL_BITS-1:0] serial_data_staging_1; |
| reg [IO_CTRL_BITS-1:0] serial_data_staging_2; |
| |
| wire serial_data_out_1; |
| wire serial_data_out_2; |
| |
| assign serial_data_out_1 = serial_data_staging_1[IO_CTRL_BITS-1]; |
| assign serial_data_out_2 = serial_data_staging_2[IO_CTRL_BITS-1]; |
| assign busy = (xfer_state != `IDLE); |
| |
| always @(posedge clk or negedge resetn) begin |
| if (resetn == 1'b0) begin |
| |
| xfer_state <= `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 <= 1'b0; |
| serial_clock <= 1'b0; |
| serial_data_staging_1 <= 0; |
| serial_data_staging_2 <= 0; |
| |
| end else begin |
| |
| if (xfer_state == `IDLE) begin |
| pad_count_1 <= `MPRJ_IO_PADS_1 - 1; |
| pad_count_2 <= `MPRJ_IO_PADS_1; |
| serial_resetn <= 1'b1; |
| serial_clock <= 1'b0; |
| if (xfer_ctrl == 1'b1) begin |
| xfer_state <= `START; |
| end |
| end else if (xfer_state == `START) begin |
| serial_resetn <= 1'b1; |
| serial_clock <= 1'b0; |
| xfer_count <= 6'd0; |
| pad_count_1 <= pad_count_1 - 1; |
| pad_count_2 <= pad_count_2 + 1; |
| xfer_state <= `XBYTE; |
| serial_data_staging_1 <= io_ctrl[pad_count_1]; |
| serial_data_staging_2 <= io_ctrl[pad_count_2]; |
| end else if (xfer_state == `XBYTE) begin |
| serial_resetn <= 1'b1; |
| serial_clock <= ~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 <= `LOAD; |
| end else begin |
| xfer_state <= `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 else if (xfer_state == `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 <= 1'b1; |
| serial_resetn <= 1'b1; |
| end else if (xfer_count == 4'd1) begin |
| serial_clock <= 1'b1; |
| serial_resetn <= 1'b0; |
| end else if (xfer_count == 4'd2) begin |
| serial_clock <= 1'b1; |
| serial_resetn <= 1'b1; |
| end else if (xfer_count == 4'd3) begin |
| serial_resetn <= 1'b1; |
| serial_clock <= 1'b0; |
| xfer_state <= `IDLE; |
| end |
| end |
| end |
| end |
| |
| endmodule |
| `default_nettype wire |