blob: 2441bf7a3f368e26a9d923906dd4a3fb9a365ca9 [file] [log] [blame]
// Any-to-any pin mux for slow speed peripherals.
//
// SPDX-FileCopyrightText: (c) 2020 Harrison Pham <harrison@harrisonpham.com>
// SPDX-License-Identifier: Apache-2.0
//
// 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.
module pinmux #(
// Number of peripheral inputs. Maximum is 255.
parameter NUM_INPUTS = 8,
// Number of peripheral outputs. Maximum is 255.
parameter NUM_OUTPUTS = 8,
// Total number of GPIOs. Maximum is 255.
parameter NUM_GPIOS = 32
) (
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_cyc_i,
input wb_stb_i,
input wb_we_i,
output reg [31:0] wb_dat_o,
output reg wb_ack_o,
input [NUM_GPIOS-1:0] gpio_in,
output [NUM_GPIOS-1:0] gpio_out,
output [NUM_GPIOS-1:0] gpio_oeb,
output [NUM_INPUTS-1:0] peripheral_in,
input [NUM_OUTPUTS-1:0] peripheral_out,
input [NUM_OUTPUTS-1:0] peripheral_oeb
);
localparam PINMUX_IN_SEL_ADDR = 16'h1000;
localparam PINMUX_OUT_SEL_ADDR = 16'h2000;
// Number of bits required for the selection registers. Theses are sized + 1
// to make room for the default selection of 0.
localparam INPUT_REG_BITS = $clog2(NUM_GPIOS + 1);
localparam OUTPUT_REG_BITS = $clog2(NUM_OUTPUTS + 1);
// Internal aliases for the input / outputs. Shifted by one so that selection
// of 0 resolves to nothing.
wire [NUM_GPIOS:0] gpio_in_int;
wire [NUM_OUTPUTS:0] peripheral_out_int;
wire [NUM_OUTPUTS:0] peripheral_oeb_int;
assign gpio_in_int = {gpio_in, 1'b0};
assign peripheral_out_int = {peripheral_out, 1'b0};
assign peripheral_oeb_int = {peripheral_oeb, 1'b1};
// Input select registers, where the register value is the GPIO number to
// route to the peripheral_in.
reg [INPUT_REG_BITS-1:0] reg_mux_in [NUM_INPUTS-1:0];
// Output select registers, where the register value is the peripheral_out
// index to route to the GPIO.
reg [OUTPUT_REG_BITS-1:0] reg_mux_out [NUM_GPIOS-1:0];
wire slave_sel;
wire slave_write_en;
assign slave_sel = (wb_stb_i && wb_cyc_i);
assign slave_write_en = (|wb_sel_i && wb_we_i);
wire pinmux_in_sel;
wire pinmux_out_sel;
assign pinmux_in_sel = |(wb_adr_i[15:0] & PINMUX_IN_SEL_ADDR);
assign pinmux_out_sel = |(wb_adr_i[15:0] & PINMUX_OUT_SEL_ADDR);
integer i;
always @(posedge wb_clk_i or posedge wb_rst_i) begin
if (wb_rst_i) begin
for (i = 0; i < NUM_INPUTS; i = i + 1) begin
reg_mux_in[i] <= {INPUT_REG_BITS{1'b0}};
end
for (i = 0; i < NUM_GPIOS; i = i + 1) begin
reg_mux_out[i] <= {OUTPUT_REG_BITS{1'b0}};
end
wb_ack_o <= 1'b0;
wb_dat_o <= 32'b0;
end else begin
wb_ack_o <= 1'b0;
if (slave_sel && !wb_ack_o) begin
wb_ack_o <= 1'b1;
if (pinmux_in_sel) begin
wb_dat_o <= reg_mux_in[wb_adr_i[INPUT_REG_BITS-1+2:0+2]];
if (slave_write_en) begin
reg_mux_in[wb_adr_i[INPUT_REG_BITS-1+2:0+2]] <=
wb_dat_i[INPUT_REG_BITS-1:0];
end
end else if (pinmux_out_sel) begin
wb_dat_o <= reg_mux_out[wb_adr_i[OUTPUT_REG_BITS-1+2:0+2]];
if (slave_write_en) begin
reg_mux_out[wb_adr_i[OUTPUT_REG_BITS-1+2:0+2]] <=
wb_dat_i[OUTPUT_REG_BITS-1:0];
end
end
end
end
end
// Generate peripheral input selection muxes.
genvar j;
generate
for (j = 0; j < NUM_INPUTS; j = j + 1) begin
assign peripheral_in[j] = gpio_in_int[reg_mux_in[j]];
end
endgenerate
// Generate peripheral output select muxes.
generate
for (j = 0; j < NUM_GPIOS; j = j + 1) begin
assign gpio_out[j] = peripheral_out_int[reg_mux_out[j]];
assign gpio_oeb[j] = peripheral_oeb_int[reg_mux_out[j]];
end
endgenerate
endmodule // module softshell_pinmux