Ethernet TX support
authorJoshua Wise <joshua@rebirth.joshuawise.com>
Sun, 25 May 2008 22:40:00 +0000 (18:40 -0400)
committerJoshua Wise <joshua@rebirth.joshuawise.com>
Sun, 25 May 2008 22:40:00 +0000 (18:40 -0400)
CoreTop.prj
CoreTop.ucf
Ethernet.v [new file with mode: 0644]
Makefile
System.v
core/GBZ80Core.v
core/insn_halt.v
ethDCM.v [new file with mode: 0644]

index c28c727..3670ba3 100644 (file)
@@ -13,3 +13,5 @@ verilog work "Sound2.v"
 verilog work "Soundcore.v"
 verilog work "Buttons.v"
 verilog work "PS2Button.v"
+verilog work "Ethernet.v"
+verilog work "ethDCM.v"
index 5e37410..f39f846 100644 (file)
@@ -1,15 +1,20 @@
-NET "xtal" LOC="B8";
+NET "xtal" LOC="B8" | CLOCK_DEDICATED_ROUTE = FALSE;   # And we get it in the ASS!
 NET "serio"  LOC = "p9";
 NET "serin" LOC = "u6";
 
-NET "leds<0>"  LOC = "j14"  ;
-NET "leds<1>"  LOC = "j15"  ;
-NET "leds<2>"  LOC = "k15"  ;
-NET "leds<3>"  LOC = "k14"  ;
-NET "leds<4>"  LOC = "e17"  ;
-NET "leds<5>"  LOC = "p15"  ;
-NET "leds<6>"  LOC = "f4"  ;
-NET "leds<7>"  LOC = "r4"  ;
+#NET "rxm" LOC="n18" ;
+#NET "rxp" LOC="p18" ;
+NET "txp" LOC="j13" ;
+NET "txm" LOC="m18" ;
+
+NET "leds<0>"  LOC = "j14" ;
+NET "leds<1>"  LOC = "j15" ;
+NET "leds<2>"  LOC = "k15" ;
+NET "leds<3>"  LOC = "k14" ;
+NET "leds<4>"  LOC = "e17" ;
+NET "leds<5>"  LOC = "p15" ;
+NET "leds<6>"  LOC = "f4" ;
+NET "leds<7>"  LOC = "r4" ;
 
 NET "switches<7>" LOC="r17";
 NET "switches<6>" LOC="n17";
diff --git a/Ethernet.v b/Ethernet.v
new file mode 100644 (file)
index 0000000..2f0c668
--- /dev/null
@@ -0,0 +1,217 @@
+`define ADDR_ETH_STATUS 16'hFF68
+`define ADDR_ETH 16'hFF69
+
+module Ethernet (
+       input clk,
+       input wr,
+       input rd,
+       input [15:0] addr,
+       inout [7:0] data,
+       input ethclk, rxclk,
+       input rxp, rxm,
+       output txp, txm,
+);
+
+       wire [10:0] outaddr;
+       wire [7:0] outdata;
+       wire busy;
+
+       reg [10:0] len = 0;
+       reg [10:0] addrcnt = 0;
+       reg [1:0] state = 0;
+       reg start = 0;
+       
+       reg [15:0] addrlatch;
+       reg rdlatch;
+
+       assign data = (addrlatch == `ADDR_ETH_STATUS && rdlatch) ? {state,5'b0,busy} : 8'bzzzzzzzz;
+
+       EnetTX tx(
+               .clk20(ethclk),
+               .Ethernet_TDp(txp),
+               .Ethernet_TDm(txm),
+               .busy(busy),
+               .length(len),
+               .rdaddress(outaddr),
+               .start(start),
+               .indata(outdata));
+       
+       EthModRam txram(
+               .wdata(data),
+               .waddr(addrcnt),
+               .raddr(outaddr),
+               .wr(wr && state == 2'b10 && addr == `ADDR_ETH),
+               .clk(clk),
+               .ethclk(ethclk),
+               .rdata(outdata)
+       );      
+       
+       always @ (posedge clk) begin
+               addrlatch <= addr;
+               rdlatch <= rd;
+               if(wr && addr == `ADDR_ETH) begin
+                       case(state)
+                       2'b00: begin
+                               len[10:8] <= data[2:0];
+                               state <= 2'b01;
+                       end
+                       2'b01: begin
+                               len[7:0] <= data[7:0];
+                               state <= 2'b10;
+                       end
+                       2'b10:
+                               if(addrcnt == len) begin
+                                       state <= 2'b11;
+                                       start <= 1'b1;
+                                       addrcnt <= 0;
+                               end else
+                                       addrcnt <= addrcnt + 1;
+                       endcase
+               end else if (state == 2'b11) begin
+                       start <= 1'b0;
+                       state <= 2'b00;
+               end
+       end
+
+endmodule
+
+module EthModRam (
+       input [7:0] wdata,
+       input [10:0] waddr,
+       input [10:0] raddr,
+       input wr,
+       input clk,
+       input ethclk,
+       output reg [7:0] rdata
+);
+
+       reg [7:0] mem [1600:0];
+
+       always @ (posedge clk) begin
+               if(wr)
+                       mem[waddr] <= wdata;
+       end
+
+       always @(posedge ethclk)
+               rdata <= mem[raddr];
+endmodule
+
+module EnetTX(input clk20, output reg Ethernet_TDp, output reg Ethernet_TDm, output wire busy, input start, input [11:0] length, output wire [11:0] rdaddress, input [7:0] indata);
+       reg StartSending; always @(posedge clk20) StartSending<=start;
+       reg [11:0] curlen = 0; always @(posedge clk20) if (start) curlen <= length + 8;
+       
+       reg [11:0] internaladdr;
+       assign rdaddress = internaladdr - 8;
+       wire [7:0] pkt_data = (internaladdr < 7) ? 8'h55 :
+                               (internaladdr == 7) ? 8'hD5 :
+                               indata;
+
+       //////////////////////////////////////////////////////////////////////
+       // and finally the 10BASE-T's magic
+       reg [3:0] ShiftCount;
+       reg SendingPacket;
+       always @(posedge clk20) if(StartSending) SendingPacket<=1; else if(ShiftCount==14 && internaladdr==(curlen + 4)) SendingPacket<=0;
+       always @(posedge clk20) ShiftCount <= SendingPacket ? ShiftCount+1 : 15;
+       wire readram = (ShiftCount==15);
+       always @(posedge clk20) if(ShiftCount==15) internaladdr <= SendingPacket ? internaladdr+1 : 0;
+       reg [7:0] ShiftData; always @(posedge clk20) if(ShiftCount[0]) ShiftData <= readram ? pkt_data : {1'b0, ShiftData[7:1]};
+
+       // generate the CRC32
+       reg [31:0] CRC;
+       reg CRCflush; always @(posedge clk20) if(CRCflush) CRCflush <= SendingPacket; else if(readram) CRCflush <= (internaladdr==curlen);
+       reg CRCinit; always @(posedge clk20) if(readram) CRCinit <= (internaladdr==7);
+       wire CRCinput = CRCflush ? 0 : (ShiftData[0] ^ CRC[31]);
+       always @(posedge clk20) if(ShiftCount[0]) CRC <= CRCinit ? ~0 : ({CRC[30:0],1'b0} ^ ({32{CRCinput}} & 32'h04C11DB7));
+
+       // generate the NLP
+       reg [17:0] LinkPulseCount; always @(posedge clk20) LinkPulseCount <= SendingPacket ? 0 : LinkPulseCount+1;
+       reg LinkPulse; always @(posedge clk20) LinkPulse <= &LinkPulseCount[17:1];
+
+       // TP_IDL, shift-register and manchester encoder
+       reg SendingPacketData; always @(posedge clk20) SendingPacketData <= SendingPacket;
+       assign busy = SendingPacketData;
+       reg [2:0] idlecount; always @(posedge clk20) if(SendingPacketData) idlecount<=0; else if(~&idlecount) idlecount<=idlecount+1;
+       wire dataout = CRCflush ? ~CRC[31] : ShiftData[0];
+       reg qo; always @(posedge clk20) qo <= SendingPacketData ? ~dataout^ShiftCount[0] : 1;
+       reg qoe; always @(posedge clk20) qoe <= SendingPacketData | LinkPulse | (idlecount<6);
+       always @(posedge clk20) Ethernet_TDp <= (qoe ? qo : 1'b0);
+       always @(posedge clk20) Ethernet_TDm <= (qoe ? ~qo : 1'b0);
+endmodule
+
+module EnetRX(
+       input rxclk,
+       input manchester_data_in,
+       output wire wr,
+       output reg [10:0] oaddr,
+       output wire [7:0] odata,
+       output reg pktrdy,
+       output reg [10:0] olength,
+       input pktclear);
+       
+       reg [2:0] in_data;
+       always @(posedge rxclk) in_data <= {in_data[1:0], manchester_data_in};
+       
+       reg [7:0] data;
+       
+       reg [1:0] cnt;
+       always @(posedge rxclk) if(|cnt || (in_data[2] ^ in_data[1])) cnt<=cnt+1;
+       
+       reg new_bit_avail;
+       always @(posedge rxclk) new_bit_avail <= (cnt==3);
+       always @(posedge rxclk) if(cnt==3) data<={in_data[1],data[7:1]};
+
+       /////////////////////////////////////////////////
+       reg end_of_Ethernet_frame;
+       
+       reg [4:0] sync1;
+       always @(posedge rxclk)
+               if(end_of_Ethernet_frame)
+                       sync1<=0; 
+               else if(new_bit_avail) begin
+                       if(!(data==8'h55 || data==8'hAA))       // not preamble?
+                               sync1 <= 0;
+                       else
+                               if(~&sync1)             // if all bits of this "sync1" counter are one, we decide that enough of the preamble
+                                                       // has been received, so stop counting and wait for "sync2" to detect the SFD
+                                       sync1 <= sync1 + 1; // otherwise keep counting
+               end
+       
+       reg [9:0] sync2;
+       always @(posedge rxclk)
+               if(end_of_Ethernet_frame || !pktrdy)
+                       sync2 <= 0;
+               else 
+               if(new_bit_avail) begin
+                       if(|sync2) // if the SFD has already been detected (Ethernet data is coming in)
+                               sync2 <= sync2 + 1; // then count the bits coming in
+                       else if(&sync1 && data==8'hD5) // otherwise, let's wait for the SFD (0xD5)
+                               sync2 <= sync2 + 1;
+               end
+
+       wire new_byte_available = new_bit_avail && (sync2[2:0]==3'h0) && (sync2[9:3]!=0);  
+
+       /////////////////////////////////////////////////
+       // if no clock transistion is detected for some time, that's the end of the Ethernet frame
+
+       reg [2:0] transition_timeout;
+       always @(posedge rxclk) if(in_data[2]^in_data[1]) transition_timeout<=0; else if(~&cnt) transition_timeout<=transition_timeout+1;
+       always @(posedge rxclk) end_of_Ethernet_frame <= &transition_timeout;
+       
+       /////////////////////////////////////////////////
+       always @(posedge rxclk)
+               if (new_byte_available && !pktrdy) begin
+                       odata <= data;
+                       oaddr <= oaddr + 1;
+                       wr <= 1;
+               end else if (end_of_Ethernet_frame) begin
+                       olength <= oaddr;
+                       oaddr <= 0;
+                       wr <= 0;
+                       pktrdy <= 1;
+               end else if (pktclear) begin
+                       pktrdy <= 0;
+                       wr <= 0;
+               else
+                       wr <= 0;
+       
+endmodule
index 2779109..2c17e40 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
 VLOGS = 7seg.v Framebuffer.v core/GBZ80Core.v Interrupt.v LCDC.v Sound1.v \
-       Sound2.v Soundcore.v System.v Timer.v Uart.v Buttons.v PS2Button.v
+       Sound2.v Soundcore.v System.v Timer.v Uart.v Buttons.v PS2Button.v \
+       Ethernet.v
 
 VLOGS_ALL = $(VLOGS) core/insn_call-callcc.v core/insn_incdec16.v \
        core/insn_jr-jrcc.v core/insn_ld_reg_hl.v core/insn_ld_reg_reg.v \
index f89b2a6..84b33b7 100644 (file)
--- a/System.v
+++ b/System.v
@@ -1,5 +1,5 @@
-
 `timescale 1ns / 1ps
+
 module SimROM(
        input [15:0] address,
        inout [7:0] data,
@@ -242,6 +242,7 @@ module CoreTop(
        output wire [22:0] cr_A,
        inout [15:0] cr_DQ,
        input ps2c, ps2d,
+       output txp, txm,
 `endif
        output wire hs, vs,
        output wire [2:0] r, g,
@@ -262,10 +263,11 @@ module CoreTop(
        wire [7:0] switches = 8'b0;
        wire [3:0] buttons = 4'b0;
 `else  
-       wire xtalb, clk, vgaclk;
+       wire xtalb, clk, vgaclk, ethclk;
        IBUFG iclkbuf(.O(xtalb), .I(xtal));
-       CPUDCM dcm (.CLKIN_IN(xtalb), .CLKFX_OUT(clk));
+       CPUDCM cpudcm (.CLKIN_IN(xtalb), .CLKFX_OUT(clk));
        pixDCM pixdcm (.CLKIN_IN(xtalb), .CLKFX_OUT(vgaclk));
+       ethDCM ethdcm (.CLKIN_IN(xtalb), .CLKFX_OUT(ethclk));
        wire [7:0] ps2buttons;
 `endif
 
@@ -471,4 +473,17 @@ module CoreTop(
                .wr(wr[0]),
                .snd_data_l(soundl),
                .snd_data_r(soundr));
+
+`ifdef isim
+`else  
+       Ethernet eth(
+               .clk(clk),
+               .wr(wr[0]),
+               .rd(rd[0]),
+               .addr(addr[0]),
+               .data(data[0]),
+               .ethclk(ethclk),
+               .txp(txp),
+               .txm(txm));
+`endif
 endmodule
index b5a502c..4a1e256 100644 (file)
@@ -233,8 +233,10 @@ module GBZ80Core(
                        end
                        wr <= 0;
                        rd <= 0;
+`ifdef isim
                        address <= 16'bxxxxxxxxxxxxxxxx;        // Make it obvious if something of type has happened.
                        wdata <= 8'bxxxxxxxx;
+`endif
                        state <= `STATE_EXECUTE;
                end
                `STATE_EXECUTE: begin
@@ -247,7 +249,10 @@ module GBZ80Core(
                        `include "allinsns.v"
                        `undef EXECUTE
                        default:
+                       begin
+                               address <= {7'h78,opcode};      // Have the CPU tell you F0xx if something's gone wrong.
                                $stop;
+                       end
                        endcase
                        state <= `STATE_WRITEBACK;
                end
index 9a3c1c0..535f7fc 100644 (file)
@@ -1,14 +1,16 @@
 `define INSN_HALT              9'b001110110
+`define INSN_STOP              9'b000010000
 
 `ifdef EXECUTE
-       `INSN_HALT: begin
+       `INSN_HALT,`INSN_STOP: begin
                `EXEC_NEWCYCLE
+               `EXEC_INC_PC
                /* XXX Interrupts needed for HALT. */
        end
 `endif
 
 `ifdef WRITEBACK
-       `INSN_HALT: begin
+       `INSN_HALT,`INSN_STOP: begin
                /* Nothing needs happen here. */
                /* XXX Interrupts needed for HALT. */
        end
diff --git a/ethDCM.v b/ethDCM.v
new file mode 100644 (file)
index 0000000..fb29dbb
--- /dev/null
+++ b/ethDCM.v
@@ -0,0 +1,76 @@
+////////////////////////////////////////////////////////////////////////////////
+// Copyright (c) 1995-2008 Xilinx, Inc.  All rights reserved.
+////////////////////////////////////////////////////////////////////////////////
+//   ____  ____ 
+//  /   /\/   / 
+// /___/  \  /    Vendor: Xilinx 
+// \   \   \/     Version : 10.1
+//  \   \         Application : xaw2verilog
+//  /   /         Filename : CPUDCM.v
+// /___/   /\     Timestamp : 03/31/2008 23:51:44
+// \   \  /  \ 
+//  \___\/\___\ 
+//
+//Command: xaw2verilog -intstyle /home/joshua/projects/fpga/FPGABoy/CPUDCM.xaw -st CPUDCM.v
+//Design Name: CPUDCM
+//Device: xc3s500e-5fg320
+//
+// Module CPUDCM
+// Generated by Xilinx Architecture Wizard
+// Written for synthesis tool: XST
+// Period Jitter (unit interval) for block DCM_SP_INST = 0.04 UI
+// Period Jitter (Peak-to-Peak) for block DCM_SP_INST = 4.90 ns
+`timescale 1ns / 1ps
+
+module ethDCM(CLKIN_IN, 
+              CLKFX_OUT, 
+              CLKIN_IBUFG_OUT, 
+              LOCKED_OUT);
+
+    input CLKIN_IN;
+   output CLKFX_OUT;
+   output CLKIN_IBUFG_OUT;
+   output LOCKED_OUT;
+   
+   wire CLKFX_BUF;
+   wire CLKIN_IBUFG;
+   wire GND_BIT;
+   
+   assign GND_BIT = 0;
+   assign CLKIN_IBUFG_OUT = CLKIN_IBUFG;
+   BUFG CLKFX_BUFG_INST (.I(CLKFX_BUF), 
+                         .O(CLKFX_OUT));
+   DCM_SP DCM_SP_INST (.CLKFB(GND_BIT), 
+                       .CLKIN(CLKIN_IN), 
+                       .DSSEN(GND_BIT), 
+                       .PSCLK(GND_BIT), 
+                       .PSEN(GND_BIT), 
+                       .PSINCDEC(GND_BIT), 
+                       .RST(GND_BIT), 
+                       .CLKDV(), 
+                       .CLKFX(CLKFX_BUF), 
+                       .CLKFX180(), 
+                       .CLK0(), 
+                       .CLK2X(), 
+                       .CLK2X180(), 
+                       .CLK90(), 
+                       .CLK180(), 
+                       .CLK270(), 
+                       .LOCKED(LOCKED_OUT), 
+                       .PSDONE(), 
+                       .STATUS());
+   defparam DCM_SP_INST.CLK_FEEDBACK = "NONE";
+   defparam DCM_SP_INST.CLKDV_DIVIDE = 2.0;
+   defparam DCM_SP_INST.CLKFX_DIVIDE = 5;
+   defparam DCM_SP_INST.CLKFX_MULTIPLY = 2;
+   defparam DCM_SP_INST.CLKIN_DIVIDE_BY_2 = "FALSE";
+   defparam DCM_SP_INST.CLKIN_PERIOD = 20.000;
+   defparam DCM_SP_INST.CLKOUT_PHASE_SHIFT = "NONE";
+   defparam DCM_SP_INST.DESKEW_ADJUST = "SYSTEM_SYNCHRONOUS";
+   defparam DCM_SP_INST.DFS_FREQUENCY_MODE = "LOW";
+   defparam DCM_SP_INST.DLL_FREQUENCY_MODE = "LOW";
+   defparam DCM_SP_INST.DUTY_CYCLE_CORRECTION = "TRUE";
+   defparam DCM_SP_INST.FACTORY_JF = 16'hC080;
+   defparam DCM_SP_INST.PHASE_SHIFT = 0;
+   defparam DCM_SP_INST.STARTUP_WAIT = "FALSE";
+endmodule
This page took 0.03673 seconds and 4 git commands to generate.