From 99b9687942367fd256a797bcd1f67fec07f92a37 Mon Sep 17 00:00:00 2001 From: Joshua Wise Date: Sun, 25 May 2008 18:40:00 -0400 Subject: [PATCH] Ethernet TX support --- CoreTop.prj | 2 + CoreTop.ucf | 23 +++-- Ethernet.v | 217 +++++++++++++++++++++++++++++++++++++++++++++++ Makefile | 3 +- System.v | 21 ++++- core/GBZ80Core.v | 5 ++ core/insn_halt.v | 6 +- ethDCM.v | 76 +++++++++++++++++ 8 files changed, 338 insertions(+), 15 deletions(-) create mode 100644 Ethernet.v create mode 100644 ethDCM.v diff --git a/CoreTop.prj b/CoreTop.prj index c28c727..3670ba3 100644 --- a/CoreTop.prj +++ b/CoreTop.prj @@ -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" diff --git a/CoreTop.ucf b/CoreTop.ucf index 5e37410..f39f846 100644 --- a/CoreTop.ucf +++ b/CoreTop.ucf @@ -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 index 0000000..2f0c668 --- /dev/null +++ b/Ethernet.v @@ -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 diff --git a/Makefile b/Makefile index 2779109..2c17e40 100644 --- 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 \ diff --git a/System.v b/System.v index f89b2a6..84b33b7 100644 --- 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 diff --git a/core/GBZ80Core.v b/core/GBZ80Core.v index b5a502c..4a1e256 100644 --- a/core/GBZ80Core.v +++ b/core/GBZ80Core.v @@ -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 diff --git a/core/insn_halt.v b/core/insn_halt.v index 9a3c1c0..535f7fc 100644 --- a/core/insn_halt.v +++ b/core/insn_halt.v @@ -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 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 -- 2.43.0