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",
"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")
#define UART_BASE 0xe0000000
#define DFII_BASE 0xe0000800
#define ID_BASE 0xe0001000
+#define MINIMAC_BASE 0xe0001800
#endif /* __CSRBASE_H */
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))
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",
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])
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
"clk4x_wr",
"clk4x_wr_strb",
"clk4x_rd",
- "clk4x_rd_strb"
+ "clk4x_rd_strb",
+ "phy_clk"
]:
s = Signal(name=name)
setattr(self, name, s)
--- /dev/null
+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()
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
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()
], [
(binc("000"), norflash0.bus),
(binc("001"), sram0.bus),
+ (binc("011"), minimac0.membus),
(binc("10"), wishbone2asmi0.wishbone),
(binc("11"), wishbone2csr0.wishbone)
],
csrcon0 = csr.Interconnect(wishbone2csr0.csr, [
uart0.bank.interface,
dfii0.bank.interface,
- identifier0.bank.interface
+ identifier0.bank.interface,
+ minimac0.bank.interface
])
#
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",
--- /dev/null
+/*
+ * 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
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
);
/*
wire pllout1;
wire pllout2;
wire pllout3;
+wire pllout4;
PLL_ADV #(
.BANDWIDTH("OPTIMIZED"),
.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),
.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(),
.I(pllout3),
.O(sys_clk)
);
+
+/* Ethernet PHY */
+always @(posedge pllout4)
+ phy_clk <= ~phy_clk;
endmodule
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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