From 4e18e456864a0a37b684e088bd33876ccfbf89c8 Mon Sep 17 00:00:00 2001 From: Sebastien Bourdeauducq Date: Sun, 20 May 2012 00:30:03 +0200 Subject: [PATCH] Add Ethernet MAC --- build.py | 2 + common/csrbase.h | 1 + constraints.py | 27 ++++- milkymist/m1crg/__init__.py | 3 +- milkymist/minimac3/__init__.py | 105 ++++++++++++++++++ top.py | 9 +- verilog/generic/psync.v | 48 ++++++++ verilog/m1crg/m1crg.v | 14 ++- verilog/minimac3/minimac3.v | 155 ++++++++++++++++++++++++++ verilog/minimac3/minimac3_memory.v | 169 +++++++++++++++++++++++++++++ verilog/minimac3/minimac3_rx.v | 146 +++++++++++++++++++++++++ verilog/minimac3/minimac3_sync.v | 93 ++++++++++++++++ verilog/minimac3/minimac3_tx.v | 100 +++++++++++++++++ 13 files changed, 863 insertions(+), 9 deletions(-) create mode 100644 milkymist/minimac3/__init__.py create mode 100644 verilog/generic/psync.v create mode 100644 verilog/minimac3/minimac3.v create mode 100644 verilog/minimac3/minimac3_memory.v create mode 100644 verilog/minimac3/minimac3_rx.v create mode 100644 verilog/minimac3/minimac3_sync.v create mode 100644 verilog/minimac3/minimac3_tx.v diff --git a/build.py b/build.py index a9ee5aae..9907effb 100644 --- a/build.py +++ b/build.py @@ -12,6 +12,7 @@ def add_core_dir(d): def add_core_files(d, files): for f in files: verilog_sources.append(os.path.join("verilog", d, f)) +add_core_dir("generic") add_core_dir("m1crg") add_core_dir("s6ddrphy") add_core_files("lm32", ["lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v", @@ -20,6 +21,7 @@ add_core_files("lm32", ["lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v "lm32_interrupt.v", "lm32_ram.v", "lm32_dp_ram.v", "lm32_icache.v", "lm32_dcache.v", "lm32_top.v", "lm32_debug.v", "lm32_jtag.v", "jtag_cores.v", "jtag_tap_spartan6.v"]) +add_core_dir("minimac3") os.chdir("build") diff --git a/common/csrbase.h b/common/csrbase.h index 76d6a567..a94d5c79 100644 --- a/common/csrbase.h +++ b/common/csrbase.h @@ -4,5 +4,6 @@ #define UART_BASE 0xe0000000 #define DFII_BASE 0xe0000800 #define ID_BASE 0xe0001000 +#define MINIMAC_BASE 0xe0001800 #endif /* __CSRBASE_H */ diff --git a/constraints.py b/constraints.py index 1214a60b..ee78a1d0 100644 --- a/constraints.py +++ b/constraints.py @@ -1,5 +1,5 @@ class Constraints: - def __init__(self, crg0, norflash0, uart0, ddrphy0): + def __init__(self, crg0, norflash0, uart0, ddrphy0, minimac0): self.constraints = [] def add(signal, pin, vec=-1, iostandard="LVCMOS33", extra=""): self.constraints.append((signal, vec, pin, iostandard, extra)) @@ -15,6 +15,7 @@ class Constraints: add(crg0.videoin_rst_n, "W17") add(crg0.flash_rst_n, "P22", extra="SLEW = FAST | DRIVE = 8") add(crg0.trigger_reset, "AA4") + add(crg0.phy_clk, "M20") add_vec(norflash0.adr, ["L22", "L20", "K22", "K21", "J19", "H20", "F22", "F21", "K17", "J17", "E22", "E20", "H18", "H19", "F20", @@ -47,6 +48,21 @@ class Constraints: extra=ddrsettings) add_vec(ddrphy0.sd_dm, ["E1", "E3", "F3", "G4"], extra=ddrsettings) add_vec(ddrphy0.sd_dqs, ["F1", "F2", "H5", "H6"], extra=ddrsettings) + + add(minimac0.phy_rst_n, "R22") + add(minimac0.phy_dv, "V21") + add(minimac0.phy_rx_clk, "H22") + add(minimac0.phy_rx_er, "V22") + add_vec(minimac0.phy_rx_data, ["U22", "U20", "T22", "T21"]) + add(minimac0.phy_tx_en, "N19") + add(minimac0.phy_tx_clk, "H21") + add(minimac0.phy_tx_er, "M19") + add_vec(minimac0.phy_tx_data, ["M16", "L15", "P19", "P20"]) + add(minimac0.phy_col, "W20") + add(minimac0.phy_crs, "W22") + + self._phy_rx_clk = minimac0.phy_rx_clk + self._phy_tx_clk = minimac0.phy_tx_clk def get_ios(self): return set([c[0] for c in self.constraints]) @@ -69,6 +85,13 @@ INST "m1crg/wr_bufpll" LOC = "BUFPLL_X0Y2"; INST "m1crg/rd_bufpll" LOC = "BUFPLL_X0Y3"; PIN "m1crg/bufg_x1.O" CLOCK_DEDICATED_ROUTE = FALSE; -""" + +NET "{phy_rx_clk}" TNM_NET = "GRPphy_rx_clk"; +NET "{phy_tx_clk}" TNM_NET = "GRPphy_tx_clk"; +TIMESPEC "TSphy_rx_clk" = PERIOD "GRPphy_rx_clk" 40 ns HIGH 50%; +TIMESPEC "TSphy_tx_clk" = PERIOD "GRPphy_tx_clk" 40 ns HIGH 50%; +TIMESPEC "TSphy_tx_clk_io" = FROM "GRPphy_tx_clk" TO "PADS" 10 ns; +TIMESPEC "TSphy_rx_clk_io" = FROM "PADS" TO "GRPphy_rx_clk" 10 ns; +""".format(phy_rx_clk=ns.get_name(self._phy_rx_clk), phy_tx_clk=ns.get_name(self._phy_tx_clk)) return r diff --git a/milkymist/m1crg/__init__.py b/milkymist/m1crg/__init__.py index a355f2ed..73e81617 100644 --- a/milkymist/m1crg/__init__.py +++ b/milkymist/m1crg/__init__.py @@ -18,7 +18,8 @@ class M1CRG: "clk4x_wr", "clk4x_wr_strb", "clk4x_rd", - "clk4x_rd_strb" + "clk4x_rd_strb", + "phy_clk" ]: s = Signal(name=name) setattr(self, name, s) diff --git a/milkymist/minimac3/__init__.py b/milkymist/minimac3/__init__.py new file mode 100644 index 00000000..75c88e31 --- /dev/null +++ b/milkymist/minimac3/__init__.py @@ -0,0 +1,105 @@ +from migen.fhdl.structure import * +from migen.bank.description import * +from migen.bank.eventmanager import * +from migen.bank import csrgen +from migen.bus import wishbone + +_count_width = 11 + +class MiniMAC: + def __init__(self, address): + # PHY signals + self.phy_tx_clk = Signal() + self.phy_tx_data = Signal(BV(4)) + self.phy_tx_en = Signal() + self.phy_tx_er = Signal() + self.phy_rx_clk = Signal() + self.phy_rx_data = Signal(BV(4)) + self.phy_dv = Signal() + self.phy_rx_er = Signal() + self.phy_col = Signal() + self.phy_crs = Signal() + self.phy_rst_n = Signal() + + # CPU interface + self._phy_reset = RegisterField("phy_reset", reset=1) + self._rx_count_0 = RegisterField("rx_count_0", _count_width, access_bus=READ_ONLY, access_dev=WRITE_ONLY) + self._rx_count_1 = RegisterField("rx_count_1", _count_width, access_bus=READ_ONLY, access_dev=WRITE_ONLY) + self._tx_count = RegisterField("tx_count", _count_width, access_dev=READ_WRITE) + regs = [self._phy_reset, self._rx_count_0, self._rx_count_1, self._tx_count] + + self._rx_event_0 = EventSourcePulse() + self._rx_event_1 = EventSourcePulse() + self._tx_event = EventSourcePulse() + self.events = EventManager(self._rx_event_0, self._rx_event_1, self._tx_event) + + self.bank = csrgen.Bank(regs + self.events.get_registers(), address=address) + self.membus = wishbone.Interface() + + def get_fragment(self): + init = Signal(reset=1) + rx_ready_0 = Signal() + rx_ready_1 = Signal() + rx_pending_0 = self._rx_event_0.pending + rx_pending_1 = self._rx_event_1.pending + rx_pending_0_r = Signal() + rx_pending_1_r = Signal() + comb = [ + self.phy_rst_n.eq(~self._phy_reset.field.r), + + rx_ready_0.eq(init | (rx_pending_0_r & ~rx_pending_0)), + rx_ready_1.eq(init | (rx_pending_1_r & ~rx_pending_1)), + + self._tx_count.field.w.eq(0), + self._tx_count.field.we.eq(self._tx_event.trigger) + ] + sync = [ + init.eq(0), + rx_pending_0_r.eq(rx_pending_0), + rx_pending_1_r.eq(rx_pending_1) + ] + inst = [ + Instance("minimac3", + [ + ("rx_done_0", self._rx_event_0.trigger), + ("rx_count_0", self._rx_count_0.field.w), + ("rx_done_1", self._rx_event_1.trigger), + ("rx_count_1", self._rx_count_1.field.w), + + ("tx_done", self._tx_event.trigger), + + ("wb_dat_o", self.membus.dat_r), + ("wb_ack_o", self.membus.ack), + + ("phy_tx_data", self.phy_tx_data), + ("phy_tx_en", self.phy_tx_en), + ("phy_tx_er", self.phy_tx_er), + ], [ + ("rx_ready_0", rx_ready_0), + ("rx_ready_1", rx_ready_1), + + ("tx_start", self._tx_count.re), + ("tx_count", self._tx_count.field.r), + + ("wb_adr_i", self.membus.adr), + ("wb_dat_i", self.membus.dat_w), + ("wb_sel_i", self.membus.sel), + ("wb_stb_i", self.membus.stb), + ("wb_cyc_i", self.membus.cyc), + ("wb_we_i", self.membus.we), + + ("phy_tx_clk", self.phy_tx_clk), + ("phy_rx_clk", self.phy_rx_clk), + ("phy_rx_data", self.phy_rx_data), + ("phy_dv", self.phy_dv), + ("phy_rx_er", self.phy_rx_er), + ("phy_col", self.phy_col), + ("phy_crs", self.phy_crs) + ], + clkport="sys_clk", + rstport="sys_rst" + ) + ] + return Fragment(comb, sync, instances=inst) \ + + self.events.get_fragment() \ + + self.bank.get_fragment() diff --git a/top.py b/top.py index 6d028bce..f8b88a40 100644 --- a/top.py +++ b/top.py @@ -5,7 +5,7 @@ from migen.fhdl.structure import * from migen.fhdl import verilog, autofragment from migen.bus import wishbone, wishbone2asmi, csr, wishbone2csr, dfi -from milkymist import m1crg, lm32, norflash, uart, sram, s6ddrphy, dfii, asmicon, identifier +from milkymist import m1crg, lm32, norflash, uart, sram, s6ddrphy, dfii, asmicon, identifier, minimac3 from cmacros import get_macros from constraints import Constraints @@ -88,6 +88,7 @@ def get(): cpu0 = lm32.LM32() norflash0 = norflash.NorFlash(25, 12) sram0 = sram.SRAM(sram_size//4) + minimac0 = minimac3.MiniMAC(csr_offset("MINIMAC")) wishbone2asmi0 = wishbone2asmi.WB2ASMI(l2_size//4, asmiport_wb) wishbone2csr0 = wishbone2csr.WB2CSR() @@ -104,6 +105,7 @@ def get(): ], [ (binc("000"), norflash0.bus), (binc("001"), sram0.bus), + (binc("011"), minimac0.membus), (binc("10"), wishbone2asmi0.wishbone), (binc("11"), wishbone2csr0.wishbone) ], @@ -118,7 +120,8 @@ def get(): csrcon0 = csr.Interconnect(wishbone2csr0.csr, [ uart0.bank.interface, dfii0.bank.interface, - identifier0.bank.interface + identifier0.bank.interface, + minimac0.bank.interface ]) # @@ -134,7 +137,7 @@ def get(): crg0 = m1crg.M1CRG(50*MHz, clk_freq) frag = autofragment.from_local() + interrupts + ddrphy_clocking(crg0, ddrphy0) - cst = Constraints(crg0, norflash0, uart0, ddrphy0) + cst = Constraints(crg0, norflash0, uart0, ddrphy0, minimac0) src_verilog, vns = verilog.convert(frag, cst.get_ios(), name="soc", diff --git a/verilog/generic/psync.v b/verilog/generic/psync.v new file mode 100644 index 00000000..bc0343f9 --- /dev/null +++ b/verilog/generic/psync.v @@ -0,0 +1,48 @@ +/* + * Milkymist SoC + * Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +module psync( + input clk1, + input i, + input clk2, + output o +); + +reg level; +always @(posedge clk1) + if(i) + level <= ~level; + +reg level1; +reg level2; +reg level3; +always @(posedge clk2) begin + level1 <= level; + level2 <= level1; + level3 <= level2; +end + +assign o = level2 ^ level3; + +initial begin + level <= 1'b0; + level1 <= 1'b0; + level2 <= 1'b0; + level3 <= 1'b0; +end + +endmodule diff --git a/verilog/m1crg/m1crg.v b/verilog/m1crg/m1crg.v index 94d00b6f..58229996 100644 --- a/verilog/m1crg/m1crg.v +++ b/verilog/m1crg/m1crg.v @@ -37,7 +37,10 @@ module m1crg #( output clk4x_wr, output clk4x_wr_strb, output clk4x_rd, - output clk4x_rd_strb + output clk4x_rd_strb, + + /* Ethernet PHY clock */ + output reg phy_clk ); /* @@ -107,6 +110,7 @@ wire pllout0; wire pllout1; wire pllout2; wire pllout3; +wire pllout4; PLL_ADV #( .BANDWIDTH("OPTIMIZED"), @@ -126,7 +130,7 @@ PLL_ADV #( .CLKOUT3_DIVIDE(4*f_div), .CLKOUT3_DUTY_CYCLE(0.5), .CLKOUT3_PHASE(0.0), - .CLKOUT4_DIVIDE(7), + .CLKOUT4_DIVIDE(4*f_mult), .CLKOUT4_DUTY_CYCLE(0.5), .CLKOUT4_PHASE(0), .CLKOUT5_DIVIDE(7), @@ -144,7 +148,7 @@ PLL_ADV #( .CLKOUT1(pllout1), /* < x4 clock for reads */ .CLKOUT2(pllout2), /* < x2 90 clock to generate memory clock, clock DQS and memory address and control signals. */ .CLKOUT3(pllout3), /* < x1 clock for system and memory controller */ - .CLKOUT4(), + .CLKOUT4(pllout4), /* < buffered clkin */ .CLKOUT5(), .CLKOUTDCM0(), .CLKOUTDCM1(), @@ -199,5 +203,9 @@ BUFG bufg_x1( .I(pllout3), .O(sys_clk) ); + +/* Ethernet PHY */ +always @(posedge pllout4) + phy_clk <= ~phy_clk; endmodule diff --git a/verilog/minimac3/minimac3.v b/verilog/minimac3/minimac3.v new file mode 100644 index 00000000..22b3d143 --- /dev/null +++ b/verilog/minimac3/minimac3.v @@ -0,0 +1,155 @@ +/* + * Milkymist SoC + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +module minimac3( + input sys_clk, + input sys_rst, + + /* Control */ + input rx_ready_0, + output rx_done_0, + output [10:0] rx_count_0, + input rx_ready_1, + output rx_done_1, + output [10:0] rx_count_1, + + input tx_start, + output tx_done, + input [10:0] tx_count, + + /* WISHBONE to access RAM */ + input [29:0] wb_adr_i, + output [31:0] wb_dat_o, + input [31:0] wb_dat_i, + input [3:0] wb_sel_i, + input wb_stb_i, + input wb_cyc_i, + output wb_ack_o, + input wb_we_i, + + /* To PHY */ + input phy_tx_clk, + output [3:0] phy_tx_data, + output phy_tx_en, + output phy_tx_er, + input phy_rx_clk, + input [3:0] phy_rx_data, + input phy_dv, + input phy_rx_er, + input phy_col, + input phy_crs +); + +wire [1:0] phy_rx_ready; +wire [1:0] phy_rx_done; +wire [10:0] phy_rx_count_0; +wire [10:0] phy_rx_count_1; +wire phy_tx_start; +wire phy_tx_done; +wire [10:0] phy_tx_count; + +minimac3_sync sync( + .sys_clk(sys_clk), + .phy_rx_clk(phy_rx_clk), + .phy_tx_clk(phy_tx_clk), + + .sys_rx_ready({rx_ready_1, rx_ready_0}), + .sys_rx_done({rx_done_1, rx_done_0}), + .sys_rx_count_0(rx_count_0), + .sys_rx_count_1(rx_count_1), + .sys_tx_start(tx_start), + .sys_tx_done(tx_done), + .sys_tx_count(tx_count), + + .phy_rx_ready(phy_rx_ready), + .phy_rx_done(phy_rx_done), + .phy_rx_count_0(phy_rx_count_0), + .phy_rx_count_1(phy_rx_count_1), + .phy_tx_start(phy_tx_start), + .phy_tx_done(phy_tx_done), + .phy_tx_count(phy_tx_count) +); + +wire [7:0] rxb0_dat; +wire [10:0] rxb0_adr; +wire rxb0_we; +wire [7:0] rxb1_dat; +wire [10:0] rxb1_adr; +wire rxb1_we; +wire [7:0] txb_dat; +wire [10:0] txb_adr; +minimac3_memory memory( + .sys_clk(sys_clk), + .sys_rst(sys_rst), + .phy_rx_clk(phy_rx_clk), + .phy_tx_clk(phy_tx_clk), + + .wb_adr_i(wb_adr_i), + .wb_dat_o(wb_dat_o), + .wb_dat_i(wb_dat_i), + .wb_sel_i(wb_sel_i), + .wb_stb_i(wb_stb_i), + .wb_cyc_i(wb_cyc_i), + .wb_ack_o(wb_ack_o), + .wb_we_i(wb_we_i), + + .rxb0_dat(rxb0_dat), + .rxb0_adr(rxb0_adr), + .rxb0_we(rxb0_we), + .rxb1_dat(rxb1_dat), + .rxb1_adr(rxb1_adr), + .rxb1_we(rxb1_we), + + .txb_dat(txb_dat), + .txb_adr(txb_adr) +); + +minimac3_tx tx( + .phy_tx_clk(phy_tx_clk), + + .tx_start(phy_tx_start), + .tx_done(phy_tx_done), + .tx_count(phy_tx_count), + .txb_dat(txb_dat), + .txb_adr(txb_adr), + + .phy_tx_en(phy_tx_en), + .phy_tx_data(phy_tx_data) +); +assign phy_tx_er = 1'b0; + +minimac3_rx rx( + .phy_rx_clk(phy_rx_clk), + + .rx_ready(phy_rx_ready), + .rx_done(phy_rx_done), + .rx_count_0(phy_rx_count_0), + .rx_count_1(phy_rx_count_1), + + .rxb0_dat(rxb0_dat), + .rxb0_adr(rxb0_adr), + .rxb0_we(rxb0_we), + .rxb1_dat(rxb1_dat), + .rxb1_adr(rxb1_adr), + .rxb1_we(rxb1_we), + + .phy_dv(phy_dv), + .phy_rx_data(phy_rx_data), + .phy_rx_er(phy_rx_er) +); + +endmodule diff --git a/verilog/minimac3/minimac3_memory.v b/verilog/minimac3/minimac3_memory.v new file mode 100644 index 00000000..57f16183 --- /dev/null +++ b/verilog/minimac3/minimac3_memory.v @@ -0,0 +1,169 @@ +/* + * Milkymist SoC + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* TODO: use behavioral BRAM models (Xst can extract byte WE) */ + +module minimac3_memory( + input sys_clk, + input sys_rst, + input phy_rx_clk, + input phy_tx_clk, + + input [29:0] wb_adr_i, + output [31:0] wb_dat_o, + input [31:0] wb_dat_i, + input [3:0] wb_sel_i, + input wb_stb_i, + input wb_cyc_i, + output reg wb_ack_o, + input wb_we_i, + + input [7:0] rxb0_dat, + input [10:0] rxb0_adr, + input rxb0_we, + input [7:0] rxb1_dat, + input [10:0] rxb1_adr, + input rxb1_we, + + output [7:0] txb_dat, + input [10:0] txb_adr + +); + +wire wb_en = wb_cyc_i & wb_stb_i; +wire [1:0] wb_buf = wb_adr_i[10:9]; +wire [31:0] wb_dat_i_le = {wb_dat_i[7:0], wb_dat_i[15:8], wb_dat_i[23:16], wb_dat_i[31:24]}; +wire [3:0] wb_sel_i_le = {wb_sel_i[0], wb_sel_i[1], wb_sel_i[2], wb_sel_i[3]}; + +wire [31:0] rxb0_wbdat; +RAMB16BWER #( + .DATA_WIDTH_A(36), + .DATA_WIDTH_B(9), + .DOA_REG(0), + .DOB_REG(0), + .EN_RSTRAM_A("FALSE"), + .EN_RSTRAM_B("FALSE"), + .SIM_DEVICE("SPARTAN6"), + .WRITE_MODE_A("WRITE_FIRST"), + .WRITE_MODE_B("WRITE_FIRST") +) rxb0 ( + .DIA(wb_dat_i_le), + .DIPA(4'd0), + .DOA(rxb0_wbdat), + .ADDRA({wb_adr_i[8:0], 5'd0}), + .WEA({4{wb_en & wb_we_i & (wb_buf == 2'b00)}} & wb_sel_i_le), + .ENA(1'b1), + .RSTA(1'b0), + .CLKA(sys_clk), + + .DIB(rxb0_dat), + .DIPB(1'd0), + .DOB(), + .ADDRB({rxb0_adr, 3'd0}), + .WEB({4{rxb0_we}}), + .ENB(1'b1), + .RSTB(1'b0), + .CLKB(phy_rx_clk) +); + +wire [31:0] rxb1_wbdat; +RAMB16BWER #( + .DATA_WIDTH_A(36), + .DATA_WIDTH_B(9), + .DOA_REG(0), + .DOB_REG(0), + .EN_RSTRAM_A("FALSE"), + .EN_RSTRAM_B("FALSE"), + .SIM_DEVICE("SPARTAN6"), + .WRITE_MODE_A("WRITE_FIRST"), + .WRITE_MODE_B("WRITE_FIRST") +) rxb1 ( + .DIA(wb_dat_i_le), + .DIPA(4'd0), + .DOA(rxb1_wbdat), + .ADDRA({wb_adr_i[8:0], 5'd0}), + .WEA({4{wb_en & wb_we_i & (wb_buf == 2'b01)}} & wb_sel_i_le), + .ENA(1'b1), + .RSTA(1'b0), + .CLKA(sys_clk), + + .DIB(rxb1_dat), + .DIPB(1'd0), + .DOB(), + .ADDRB({rxb1_adr, 3'd0}), + .WEB({4{rxb1_we}}), + .ENB(1'b1), + .RSTB(1'b0), + .CLKB(phy_rx_clk) +); + +wire [31:0] txb_wbdat; +RAMB16BWER #( + .DATA_WIDTH_A(36), + .DATA_WIDTH_B(9), + .DOA_REG(0), + .DOB_REG(0), + .EN_RSTRAM_A("FALSE"), + .EN_RSTRAM_B("FALSE"), + .SIM_DEVICE("SPARTAN6"), + .WRITE_MODE_A("WRITE_FIRST"), + .WRITE_MODE_B("WRITE_FIRST") +) txb ( + .DIA(wb_dat_i_le), + .DIPA(4'd0), + .DOA(txb_wbdat), + .ADDRA({wb_adr_i[8:0], 5'd0}), + .WEA({4{wb_en & wb_we_i & (wb_buf == 2'b10)}} & wb_sel_i_le), + .ENA(1'b1), + .RSTA(1'b0), + .CLKA(sys_clk), + + .DIB(8'd0), + .DIPB(1'd0), + .DOB(txb_dat), + .ADDRB({txb_adr, 3'd0}), + .WEB(4'd0), + .ENB(1'b1), + .RSTB(1'b0), + .CLKB(phy_tx_clk) +); + +always @(posedge sys_clk) begin + if(sys_rst) + wb_ack_o <= 1'b0; + else begin + wb_ack_o <= 1'b0; + if(wb_en & ~wb_ack_o) + wb_ack_o <= 1'b1; + end +end + +reg [1:0] wb_buf_r; +always @(posedge sys_clk) + wb_buf_r <= wb_buf; + +reg [31:0] wb_dat_o_le; +always @(*) begin + case(wb_buf_r) + 2'b00: wb_dat_o_le = rxb0_wbdat; + 2'b01: wb_dat_o_le = rxb1_wbdat; + default: wb_dat_o_le = txb_wbdat; + endcase +end +assign wb_dat_o = {wb_dat_o_le[7:0], wb_dat_o_le[15:8], wb_dat_o_le[23:16], wb_dat_o_le[31:24]}; + +endmodule diff --git a/verilog/minimac3/minimac3_rx.v b/verilog/minimac3/minimac3_rx.v new file mode 100644 index 00000000..70cc7426 --- /dev/null +++ b/verilog/minimac3/minimac3_rx.v @@ -0,0 +1,146 @@ +/* + * Milkymist SoC + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +module minimac3_rx( + input phy_rx_clk, + + input [1:0] rx_ready, + output [1:0] rx_done, + output reg [10:0] rx_count_0, + output reg [10:0] rx_count_1, + + output [7:0] rxb0_dat, + output [10:0] rxb0_adr, + output rxb0_we, + output [7:0] rxb1_dat, + output [10:0] rxb1_adr, + output rxb1_we, + + input phy_dv, + input [3:0] phy_rx_data, + input phy_rx_er +); + +reg [1:0] available_slots; +always @(posedge phy_rx_clk) + available_slots <= (available_slots & ~rx_done) | rx_ready; +initial available_slots <= 2'd0; + +reg [1:0] used_slot; +reg used_slot_update; +always @(posedge phy_rx_clk) begin + if(used_slot_update) begin + used_slot[0] <= available_slots[0]; + used_slot[1] <= available_slots[1] & ~available_slots[0]; + end +end + +reg rx_done_ctl; +assign rx_done = {2{rx_done_ctl}} & used_slot; + +reg rx_count_reset_ctl; +reg rx_count_inc_ctl; +wire [1:0] rx_count_reset = {2{rx_count_reset_ctl}} & used_slot; +wire [1:0] rx_count_inc = {2{rx_count_inc_ctl}} & used_slot; +always @(posedge phy_rx_clk) begin + if(rx_count_reset[0]) + rx_count_0 <= 11'd0; + else if(rx_count_inc[0]) + rx_count_0 <= rx_count_0 + 11'd1; + if(rx_count_reset[1]) + rx_count_1 <= 11'd0; + else if(rx_count_inc[1]) + rx_count_1 <= rx_count_1 + 11'd1; +end + +assign rxb0_adr = rx_count_0; +assign rxb1_adr = rx_count_1; +reg rxb_we_ctl; +assign rxb0_we = rxb_we_ctl & used_slot[0]; +assign rxb1_we = rxb_we_ctl & used_slot[1]; + +reg [3:0] lo; +reg [3:0] hi; +reg [1:0] load_nibble; +always @(posedge phy_rx_clk) begin + if(load_nibble[0]) + lo <= phy_rx_data; + if(load_nibble[1]) + hi <= phy_rx_data; +end +assign rxb0_dat = {hi, lo}; +assign rxb1_dat = {hi, lo}; + +reg [1:0] state; +reg [1:0] next_state; + +parameter IDLE = 2'd0; +parameter LOAD_LO = 2'd1; +parameter LOAD_HI = 2'd2; +parameter TERMINATE = 2'd3; + +initial state <= IDLE; +always @(posedge phy_rx_clk) + state <= next_state; + +always @(*) begin + used_slot_update = 1'b0; + rx_done_ctl = 1'b0; + rx_count_reset_ctl = 1'b0; + rx_count_inc_ctl = 1'b0; + rxb_we_ctl = 1'b0; + load_nibble = 2'b00; + + next_state = state; + case(state) + IDLE: begin + used_slot_update = 1'b1; + if(phy_dv) begin + rx_count_reset_ctl = 1'b1; + used_slot_update = 1'b0; + load_nibble = 2'b01; + next_state = LOAD_HI; + end + end + LOAD_LO: begin + rxb_we_ctl = 1'b1; + rx_count_inc_ctl = 1'b1; + if(phy_dv) begin + load_nibble = 2'b01; + next_state = LOAD_HI; + end else begin + rx_done_ctl = 1'b1; + next_state = TERMINATE; + end + end + LOAD_HI: begin + if(phy_dv) begin + load_nibble = 2'b10; + next_state = LOAD_LO; + end else begin + rx_done_ctl = 1'b1; + next_state = TERMINATE; + end + end + TERMINATE: begin + used_slot_update = 1'b1; + next_state = IDLE; + end + endcase +end + +endmodule diff --git a/verilog/minimac3/minimac3_sync.v b/verilog/minimac3/minimac3_sync.v new file mode 100644 index 00000000..83b7bae6 --- /dev/null +++ b/verilog/minimac3/minimac3_sync.v @@ -0,0 +1,93 @@ +/* + * Milkymist SoC + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +module minimac3_sync( + input sys_clk, + input phy_rx_clk, + input phy_tx_clk, + + input [1:0] sys_rx_ready, + output [1:0] sys_rx_done, + output reg [10:0] sys_rx_count_0, + output reg [10:0] sys_rx_count_1, + + input sys_tx_start, + output sys_tx_done, + input [10:0] sys_tx_count, + + output [1:0] phy_rx_ready, + input [1:0] phy_rx_done, + input [10:0] phy_rx_count_0, + input [10:0] phy_rx_count_1, + + output phy_tx_start, + input phy_tx_done, + output reg [10:0] phy_tx_count +); + +psync rx_ready_0( + .clk1(sys_clk), + .i(sys_rx_ready[0]), + .clk2(phy_rx_clk), + .o(phy_rx_ready[0]) +); +psync rx_ready_1( + .clk1(sys_clk), + .i(sys_rx_ready[1]), + .clk2(phy_rx_clk), + .o(phy_rx_ready[1]) +); +psync rx_done_0( + .clk1(phy_rx_clk), + .i(phy_rx_done[0]), + .clk2(sys_clk), + .o(sys_rx_done[0]) +); +psync rx_done_1( + .clk1(phy_rx_clk), + .i(phy_rx_done[1]), + .clk2(sys_clk), + .o(sys_rx_done[1]) +); +reg [10:0] sys_rx_count_0_r; +reg [10:0] sys_rx_count_1_r; +always @(posedge sys_clk) begin + sys_rx_count_0_r <= phy_rx_count_0; + sys_rx_count_0 <= sys_rx_count_0_r; + sys_rx_count_1_r <= phy_rx_count_1; + sys_rx_count_1 <= sys_rx_count_1_r; +end + +psync tx_start( + .clk1(sys_clk), + .i(sys_tx_start), + .clk2(phy_tx_clk), + .o(phy_tx_start) +); +psync tx_done( + .clk1(phy_tx_clk), + .i(phy_tx_done), + .clk2(sys_clk), + .o(sys_tx_done) +); +reg [10:0] phy_tx_count_r; +always @(posedge phy_tx_clk) begin + phy_tx_count_r <= sys_tx_count; + phy_tx_count <= phy_tx_count_r; +end + +endmodule diff --git a/verilog/minimac3/minimac3_tx.v b/verilog/minimac3/minimac3_tx.v new file mode 100644 index 00000000..46ab2163 --- /dev/null +++ b/verilog/minimac3/minimac3_tx.v @@ -0,0 +1,100 @@ +/* + * Milkymist SoC + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Sebastien Bourdeauducq + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +module minimac3_tx( + input phy_tx_clk, + + input tx_start, + output reg tx_done, + input [10:0] tx_count, + input [7:0] txb_dat, + output [10:0] txb_adr, + + output reg phy_tx_en, + output reg [3:0] phy_tx_data +); + +reg phy_tx_en_r; +reg phy_tx_data_sel; +wire [3:0] phy_tx_data_r = phy_tx_data_sel ? txb_dat[7:4] : txb_dat[3:0]; +always @(posedge phy_tx_clk) begin + phy_tx_en <= phy_tx_en_r; + phy_tx_data <= phy_tx_data_r; +end + +reg [10:0] byte_count; +reg byte_count_reset; +reg byte_count_inc; +always @(posedge phy_tx_clk) begin + if(byte_count_reset) + byte_count <= 11'd0; + else if(byte_count_inc) + byte_count <= byte_count + 11'd1; +end +assign txb_adr = byte_count; +wire byte_count_max = byte_count == tx_count; + +parameter IDLE = 2'd0; +parameter SEND_LO = 2'd1; +parameter SEND_HI = 2'd2; +parameter TERMINATE = 2'd3; + +reg [1:0] state; +reg [1:0] next_state; + +initial state <= IDLE; +always @(posedge phy_tx_clk) + state <= next_state; + +always @(*) begin + phy_tx_en_r = 1'b0; + phy_tx_data_sel = 1'b0; + byte_count_reset = 1'b0; + byte_count_inc = 1'b0; + tx_done = 1'b0; + + next_state = state; + + case(state) + IDLE: begin + byte_count_reset = 1'b1; + if(tx_start) + next_state = SEND_LO; + end + SEND_LO: begin + byte_count_inc = 1'b1; + phy_tx_en_r = 1'b1; + phy_tx_data_sel = 1'b0; + next_state = SEND_HI; + end + SEND_HI: begin + phy_tx_en_r = 1'b1; + phy_tx_data_sel = 1'b1; + if(byte_count_max) + next_state = TERMINATE; + else + next_state = SEND_LO; + end + TERMINATE: begin + byte_count_reset = 1'b1; + tx_done = 1'b1; + next_state = IDLE; + end + endcase +end + +endmodule -- 2.30.2