Add Ethernet MAC
authorSebastien Bourdeauducq <sebastien@milkymist.org>
Sat, 19 May 2012 22:30:03 +0000 (00:30 +0200)
committerSebastien Bourdeauducq <sebastien@milkymist.org>
Sat, 19 May 2012 22:30:03 +0000 (00:30 +0200)
13 files changed:
build.py
common/csrbase.h
constraints.py
milkymist/m1crg/__init__.py
milkymist/minimac3/__init__.py [new file with mode: 0644]
top.py
verilog/generic/psync.v [new file with mode: 0644]
verilog/m1crg/m1crg.v
verilog/minimac3/minimac3.v [new file with mode: 0644]
verilog/minimac3/minimac3_memory.v [new file with mode: 0644]
verilog/minimac3/minimac3_rx.v [new file with mode: 0644]
verilog/minimac3/minimac3_sync.v [new file with mode: 0644]
verilog/minimac3/minimac3_tx.v [new file with mode: 0644]

index a9ee5aae3cceb75b63ea436dee4c3c3448106d0b..9907effbaf78f30572a036bdefa2b5cc52ad82c6 100644 (file)
--- 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")
 
index 76d6a567177617a1b95f6630588af03bb0d58a68..a94d5c791dd44d22d7a654ceb6896494ed787d69 100644 (file)
@@ -4,5 +4,6 @@
 #define UART_BASE      0xe0000000
 #define DFII_BASE      0xe0000800
 #define ID_BASE                0xe0001000
+#define MINIMAC_BASE   0xe0001800
 
 #endif /* __CSRBASE_H */
index 1214a60beb1d6ca3807c8c3e89a27b3297aa6aa4..ee78a1d0ee6ac6ebbdad60513aa77c1ceebe6b49 100644 (file)
@@ -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
index a355f2edcdcfb352d0540622283d6060e839c460..73e81617d35cbb33d2179d3024a4ef0575087ce3 100644 (file)
@@ -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 (file)
index 0000000..75c88e3
--- /dev/null
@@ -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 6d028bcefc3213c184594a42a40911186bf4c585..f8b88a400c8da517b262aa93c42fba27ad3a4ef6 100644 (file)
--- 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 (file)
index 0000000..bc0343f
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+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
index 94d00b6f4725d15217a93f3163dc9a9439f36862..5822999634deba86ca4e2c47b537908b431aa9d0 100644 (file)
@@ -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 (file)
index 0000000..22b3d14
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+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 (file)
index 0000000..57f1618
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+/* 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 (file)
index 0000000..70cc742
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+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 (file)
index 0000000..83b7bae
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+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 (file)
index 0000000..46ab216
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+ */
+
+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