1//
2// Copyright 2011 Ettus Research LLC
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16//
17
18
19module wb_reg
20  #(parameter ADDR=0,
21    parameter DEFAULT=0,
22    parameter WIDTH=32)
23   (input clk, input rst,
24    input [5:0] adr, input wr_acc,
25    input [31:0] dat_i, output reg [WIDTH-1:0] dat_o);
26
27   always @(posedge clk)
28     if(rst)
29       dat_o <= DEFAULT;
30     else if(wr_acc & (adr == ADDR))
31       dat_o <= dat_i[WIDTH-1:0];
32
33endmodule // wb_reg
34
35
36
37module simple_gemac_wb
38  (input wb_clk, input wb_rst,
39   input wb_cyc, input wb_stb, output reg wb_ack, input wb_we,
40   input [7:0] wb_adr, input [31:0] wb_dat_i, output reg [31:0] wb_dat_o,
41
42   inout mdio, output mdc,
43   output [47:0] ucast_addr, output [47:0] mcast_addr,
44   output pass_ucast, output pass_mcast, output pass_bcast,
45   output pass_pause, output pass_all,
46   output pause_respect_en, output pause_request_en,
47   output [15:0] pause_time, output [15:0] pause_thresh  );
48
49   wire   acc 	  = wb_cyc & wb_stb;
50   wire   wr_acc  = wb_cyc & wb_stb & wb_we;
51   wire   rd_acc  = wb_cyc & wb_stb & ~wb_we;
52
53   always @(posedge wb_clk)
54     if(wb_rst)
55       wb_ack 	 <= 0;
56     else
57       wb_ack 	 <= acc & ~wb_ack;
58
59   wire [6:0] misc_settings;
60   assign {pause_request_en, pass_ucast, pass_mcast, pass_bcast, pass_pause, pass_all, pause_respect_en} = misc_settings;
61
62   wb_reg #(.ADDR(0),.DEFAULT(7'b0111011),.WIDTH(7))
63   wb_reg_settings (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
64		    .dat_i(wb_dat_i), .dat_o(misc_settings) );
65   wb_reg #(.ADDR(1),.DEFAULT(0),.WIDTH(16))
66   wb_reg_ucast_h (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
67		   .dat_i(wb_dat_i), .dat_o(ucast_addr[47:32]) );
68   wb_reg #(.ADDR(2),.DEFAULT(0),.WIDTH(32))
69   wb_reg_ucast_l (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
70		   .dat_i(wb_dat_i), .dat_o(ucast_addr[31:0]) );
71   wb_reg #(.ADDR(3),.DEFAULT(0),.WIDTH(16))
72   wb_reg_mcast_h (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
73		   .dat_i(wb_dat_i), .dat_o(mcast_addr[47:32]) );
74   wb_reg #(.ADDR(4),.DEFAULT(0),.WIDTH(32))
75   wb_reg_mcast_l (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
76		   .dat_i(wb_dat_i), .dat_o(mcast_addr[31:0]) );
77
78   //MII to CPU
79   wire [7:0] Divider;            // Divider for the host clock
80   wire [15:0] CtrlData;          // Control Data (to be written to the PHY reg.)
81   wire [4:0]  Rgad;               // Register Address (within the PHY)
82   wire [4:0]  Fiad;               // PHY Address
83   wire        NoPre;              // No Preamble (no 32-bit preamble)
84   wire        WCtrlData;          // Write Control Data operation
85   wire        RStat;              // Read Status operation
86   wire        ScanStat;           // Scan Status operation
87   wire        Busy;               // Busy Signal
88   wire        LinkFail;           // Link Integrity Signal
89   wire        Nvalid;             // Invalid Status (qualifier for the valid scan result)
90   wire [15:0] Prsd;               // Read Status Data (data read from the PHY)
91   wire        WCtrlDataStart;     // This signals resets the WCTRLDATA bit in the MIIM Command register
92   wire        RStatStart;         // This signal resets the RSTAT BIT in the MIIM Command register
93   wire        UpdateMIIRX_DATAReg; // Updates MII RX_DATA register with read data
94
95   // registers for controlling the MII interface
96   reg [2:0]   MIICOMMAND;
97   wire [12:0] MIIADDRESS;
98   reg [15:0]  MIIRX_DATA;
99   wire [2:0]  MIISTATUS;
100
101   wb_reg #(.ADDR(5),.DEFAULT(0),.WIDTH(9))
102   wb_reg_miimoder (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
103		    .dat_i(wb_dat_i), .dat_o({NoPre,Divider}) );
104
105   wb_reg #(.ADDR(6),.DEFAULT(0),.WIDTH(13))
106   wb_reg_miiaddr (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
107		   .dat_i(wb_dat_i), .dat_o(MIIADDRESS) );
108
109   wb_reg #(.ADDR(7),.DEFAULT(0),.WIDTH(16))
110   wb_reg_miidata (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
111		   .dat_i(wb_dat_i), .dat_o(CtrlData) );
112
113   // MIICOMMAND register - needs special treatment because of auto-resetting bits
114   always @ (posedge wb_clk)
115     if (wb_rst)
116       MIICOMMAND <= 0;
117     else
118       if (wr_acc & (wb_adr[7:2] == 6'd8))
119         MIICOMMAND <= wb_dat_i;
120       else
121         begin
122            if ( WCtrlDataStart )
123              MIICOMMAND[2] <= 0;
124            if ( RStatStart )
125              MIICOMMAND[1] <= 0;
126         end
127
128   // MIIRX_DATA register
129   always @(posedge wb_clk)
130     if (wb_rst)
131       MIIRX_DATA <= 0;
132     else
133       if (UpdateMIIRX_DATAReg )
134         MIIRX_DATA <= Prsd;
135
136   // MIICOMMAND
137   assign WCtrlData  = MIICOMMAND[2];
138   assign RStat      = MIICOMMAND[1];
139   assign ScanStat   = MIICOMMAND[0];
140   // MIIADDRESS
141   assign Rgad 	     = MIIADDRESS[12:8];
142   assign Fiad 	     = MIIADDRESS[4:0];
143   // MIISTATUS
144   assign MIISTATUS[2:0] = { Nvalid, Busy, LinkFail };
145
146   eth_miim eth_miim
147     (.Clk(wb_clk), .Reset(wb_rst),
148      .Divider(Divider), .NoPre(NoPre), .CtrlData(CtrlData), .Rgad(Rgad), .Fiad(Fiad),
149      .WCtrlData(WCtrlData), .RStat(RStat), .ScanStat(ScanStat), .Mdio(mdio), .Mdc(mdc),
150      .Busy(Busy), .Prsd(Prsd), .LinkFail(LinkFail), .Nvalid(Nvalid),
151      .WCtrlDataStart(WCtrlDataStart), .RStatStart(RStatStart),
152      .UpdateMIIRX_DATAReg(UpdateMIIRX_DATAReg) );
153
154   wb_reg #(.ADDR(11),.DEFAULT(0),.WIDTH(16))
155   wb_reg_pausetime (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
156		     .dat_i(wb_dat_i), .dat_o(pause_time) );
157
158   wb_reg #(.ADDR(12),.DEFAULT(0),.WIDTH(16))
159   wb_reg_pausethresh (.clk(wb_clk), .rst(wb_rst), .adr(wb_adr[7:2]), .wr_acc(wr_acc),
160		       .dat_i(wb_dat_i), .dat_o(pause_thresh) );
161
162   always @(posedge wb_clk)
163     case(wb_adr[7:2])
164       //0 : wb_dat_o <= misc_settings;
165       //1 : wb_dat_o <= ucast_addr[47:32];
166       //2 : wb_dat_o <= ucast_addr[31:0];
167       //3 : wb_dat_o <= mcast_addr[47:32];
168       //4 : wb_dat_o <= mcast_addr[31:0];
169       //5 : wb_dat_o <= {NoPre,Divider};
170       //6 : wb_dat_o <= MIIADDRESS;
171       //7 : wb_dat_o <= CtrlData;
172       8 : wb_dat_o <= MIICOMMAND;
173       9 : wb_dat_o <= MIISTATUS;
174       10: wb_dat_o <= MIIRX_DATA;
175       //11: wb_dat_o <= pause_time;
176       //12: wb_dat_o <= pause_thresh;
177     endcase // case (wb_adr[7:2])
178
179endmodule // simple_gemac_wb
180