mirror of
https://github.com/fpganinja/taxi.git
synced 2025-12-07 16:28:40 -08:00
lss: Add MDIO master
Signed-off-by: Alex Forencich <alex@alexforencich.com>
This commit is contained in:
@@ -85,6 +85,7 @@ To facilitate the dual-license model, contributions to the project can only be a
|
||||
* LFSR self-synchronizing descrambler
|
||||
* Low-speed serial
|
||||
* UART
|
||||
* MDIO master
|
||||
* Primitives
|
||||
* Arbiter
|
||||
* Priority encoder
|
||||
|
||||
221
rtl/lss/taxi_mdio_master.sv
Normal file
221
rtl/lss/taxi_mdio_master.sv
Normal file
@@ -0,0 +1,221 @@
|
||||
// SPDX-License-Identifier: CERN-OHL-S-2.0
|
||||
/*
|
||||
|
||||
Copyright (c) 2015-2025 FPGA Ninja, LLC
|
||||
|
||||
Authors:
|
||||
- Alex Forencich
|
||||
|
||||
*/
|
||||
|
||||
`resetall
|
||||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
/*
|
||||
* MDIO master
|
||||
*/
|
||||
module taxi_mdio_master (
|
||||
input wire logic clk,
|
||||
input wire logic rst,
|
||||
|
||||
/*
|
||||
* Host interface
|
||||
*/
|
||||
taxi_axis_if.snk s_axis_cmd,
|
||||
taxi_axis_if.src m_axis_rd_data,
|
||||
|
||||
/*
|
||||
* MDIO to PHY
|
||||
*/
|
||||
output wire logic mdc_o,
|
||||
input wire logic mdio_i,
|
||||
output wire logic mdio_o,
|
||||
output wire logic mdio_t,
|
||||
|
||||
/*
|
||||
* Status
|
||||
*/
|
||||
output wire logic busy,
|
||||
|
||||
/*
|
||||
* Configuration
|
||||
*/
|
||||
input wire logic [7:0] prescale
|
||||
);
|
||||
|
||||
localparam [1:0]
|
||||
STATE_IDLE = 2'd0,
|
||||
STATE_PREAMBLE = 2'd1,
|
||||
STATE_TRANSFER = 2'd2;
|
||||
|
||||
logic [1:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
logic [7:0] count_reg = '0, count_next;
|
||||
logic [5:0] bit_count_reg = '0, bit_count_next;
|
||||
logic cycle_reg = 1'b0, cycle_next;
|
||||
|
||||
logic [31:0] data_reg = '0, data_next;
|
||||
|
||||
logic [1:0] op_reg = 2'b00, op_next;
|
||||
|
||||
logic s_axis_cmd_ready_reg = 1'b0, cmd_ready_next;
|
||||
|
||||
logic [15:0] m_axis_rd_data_reg = '0, m_axis_rd_data_next;
|
||||
logic m_axis_rd_data_valid_reg = 1'b0, m_axis_rd_data_valid_next;
|
||||
|
||||
logic mdio_i_reg = 1'b1;
|
||||
|
||||
logic mdc_o_reg = 1'b0, mdc_o_next;
|
||||
logic mdio_o_reg = 1'b0, mdio_o_next;
|
||||
logic mdio_t_reg = 1'b1, mdio_t_next;
|
||||
|
||||
logic busy_reg = 1'b0;
|
||||
|
||||
assign s_axis_cmd.tready = s_axis_cmd_ready_reg;
|
||||
|
||||
assign m_axis_rd_data.tdata = m_axis_rd_data_reg;
|
||||
assign m_axis_rd_data.tkeep = '1;
|
||||
assign m_axis_rd_data.tstrb = m_axis_rd_data.tkeep;
|
||||
assign m_axis_rd_data.tvalid = m_axis_rd_data_valid_reg;
|
||||
assign m_axis_rd_data.tlast = 1'b1;
|
||||
assign m_axis_rd_data.tid = '0;
|
||||
assign m_axis_rd_data.tdest = '0;
|
||||
assign m_axis_rd_data.tuser = '0;
|
||||
|
||||
assign mdc_o = mdc_o_reg;
|
||||
assign mdio_o = mdio_o_reg;
|
||||
assign mdio_t = mdio_t_reg;
|
||||
|
||||
assign busy = busy_reg;
|
||||
|
||||
wire [1:0] cmd_st = s_axis_cmd.tdata[31:30];
|
||||
wire [1:0] cmd_op = s_axis_cmd.tdata[29:28];
|
||||
wire [9:0] cmd_addr = s_axis_cmd.tdata[27:18];
|
||||
wire [15:0] cmd_data = s_axis_cmd.tdata[15:0];
|
||||
|
||||
always_comb begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
count_next = count_reg;
|
||||
bit_count_next = bit_count_reg;
|
||||
cycle_next = cycle_reg;
|
||||
|
||||
data_next = data_reg;
|
||||
|
||||
op_next = op_reg;
|
||||
|
||||
cmd_ready_next = 1'b0;
|
||||
|
||||
m_axis_rd_data_next = m_axis_rd_data_reg;
|
||||
m_axis_rd_data_valid_next = m_axis_rd_data_valid_reg && !m_axis_rd_data.tready;
|
||||
|
||||
mdc_o_next = mdc_o_reg;
|
||||
mdio_o_next = mdio_o_reg;
|
||||
mdio_t_next = mdio_t_reg;
|
||||
|
||||
if (count_reg != 0) begin
|
||||
count_next = count_reg - 8'd1;
|
||||
state_next = state_reg;
|
||||
end else if (cycle_reg) begin
|
||||
cycle_next = 1'b0;
|
||||
mdc_o_next = 1'b1;
|
||||
count_next = prescale;
|
||||
state_next = state_reg;
|
||||
end else begin
|
||||
mdc_o_next = 1'b0;
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle - accept new command
|
||||
if (s_axis_cmd.tvalid) begin
|
||||
cmd_ready_next = 1'b1;
|
||||
data_next = {cmd_st, cmd_op, cmd_addr, 2'b10, cmd_data};
|
||||
op_next = cmd_op;
|
||||
mdio_t_next = 1'b0;
|
||||
mdio_o_next = 1'b1;
|
||||
bit_count_next = 6'd32;
|
||||
cycle_next = 1'b1;
|
||||
count_next = prescale;
|
||||
state_next = STATE_PREAMBLE;
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_PREAMBLE: begin
|
||||
cycle_next = 1'b1;
|
||||
count_next = prescale;
|
||||
if (bit_count_reg > 6'd1) begin
|
||||
bit_count_next = bit_count_reg - 6'd1;
|
||||
state_next = STATE_PREAMBLE;
|
||||
end else begin
|
||||
bit_count_next = 6'd32;
|
||||
{mdio_o_next, data_next} = {data_reg, mdio_i_reg};
|
||||
state_next = STATE_TRANSFER;
|
||||
end
|
||||
end
|
||||
STATE_TRANSFER: begin
|
||||
cycle_next = 1'b1;
|
||||
count_next = prescale;
|
||||
if (op_reg[1] && bit_count_reg == 6'd19) begin
|
||||
mdio_t_next = 1'b1;
|
||||
end
|
||||
if (bit_count_reg > 6'd1) begin
|
||||
bit_count_next = bit_count_reg - 6'd1;
|
||||
{mdio_o_next, data_next} = {data_reg, mdio_i_reg};
|
||||
state_next = STATE_TRANSFER;
|
||||
end else begin
|
||||
if (op_reg[1]) begin
|
||||
m_axis_rd_data_next = data_reg[15:0];
|
||||
m_axis_rd_data_valid_next = 1'b1;
|
||||
end
|
||||
mdio_t_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
default: begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
state_reg <= state_next;
|
||||
|
||||
count_reg <= count_next;
|
||||
bit_count_reg <= bit_count_next;
|
||||
cycle_reg <= cycle_next;
|
||||
|
||||
data_reg <= data_next;
|
||||
op_reg <= op_next;
|
||||
|
||||
s_axis_cmd_ready_reg <= cmd_ready_next;
|
||||
|
||||
m_axis_rd_data_reg <= m_axis_rd_data_next;
|
||||
m_axis_rd_data_valid_reg <= m_axis_rd_data_valid_next;
|
||||
|
||||
mdio_i_reg <= mdio_i;
|
||||
|
||||
mdc_o_reg <= mdc_o_next;
|
||||
mdio_o_reg <= mdio_o_next;
|
||||
mdio_t_reg <= mdio_t_next;
|
||||
|
||||
busy_reg <= (state_next != STATE_IDLE || count_reg != 0 || cycle_reg || mdc_o);
|
||||
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
count_reg <= '0;
|
||||
bit_count_reg <= '0;
|
||||
cycle_reg <= 1'b0;
|
||||
s_axis_cmd_ready_reg <= 1'b0;
|
||||
m_axis_rd_data_valid_reg <= 1'b0;
|
||||
mdc_o_reg <= 1'b0;
|
||||
mdio_o_reg <= 1'b0;
|
||||
mdio_t_reg <= 1'b1;
|
||||
busy_reg <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`resetall
|
||||
Reference in New Issue
Block a user