瀏覽代碼

cores/ice40: Import architecture specific utility cores

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 5 年之前
父節點
當前提交
f8d8427821
共有 6 個文件被更改,包括 552 次插入0 次删除
  1. 3 0
      cores/ice40/Makefile
  2. 7 0
      cores/ice40/README.md
  3. 12 0
      cores/ice40/core.mk
  4. 220 0
      cores/ice40/rtl/ice40_ebr.v
  5. 172 0
      cores/ice40/rtl/ice40_spram_gen.v
  6. 138 0
      cores/ice40/sim/ice40_ebr_tb.v

+ 3 - 0
cores/ice40/Makefile

@@ -0,0 +1,3 @@
+CORE := ice40
+
+include ../../build/core-rules.mk

+ 7 - 0
cores/ice40/README.md

@@ -0,0 +1,7 @@
+iCE40
+=====
+
+This contains a collection of utility cores specifically targetting the
+lattice iCE40 FPGA family.
+
+These cores are licensed under the BSD 3-clause licence (see LICENSE.bsd)

+ 12 - 0
cores/ice40/core.mk

@@ -0,0 +1,12 @@
+CORE := ice40
+
+RTL_SRCS_ice40 := $(addprefix rtl/, \
+	ice40_ebr.v \
+	ice40_spram_gen.v \
+)
+
+TESTBENCHES_ice40 := \
+	ice40_ebr_tb \
+	$(NULL)
+
+include $(ROOT)/build/core-magic.mk

+ 220 - 0
cores/ice40/rtl/ice40_ebr.v

@@ -0,0 +1,220 @@
+/*
+ * ice40_ebr.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
+ * All rights reserved.
+ *
+ * BSD 3-clause, see LICENSE.bsd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+`default_nettype none
+
+module ice40_ebr #(
+	parameter integer READ_MODE  = 0,	/* 0 =  256x16, 1 =  512x8 */
+	parameter integer WRITE_MODE = 0,	/* 2 = 1024x4,  3 = 2048x2 */
+	parameter integer MASK_WORKAROUND = 0,
+	parameter integer NEG_WR_CLK = 0,
+	parameter integer NEG_RD_CLK = 0,
+	parameter INIT_FILE = "",
+
+	// auto
+	parameter integer WAW = 8 + WRITE_MODE,
+	parameter integer WDW = 16 / (1 << WRITE_MODE),
+	parameter integer RAW = 8 + READ_MODE,
+	parameter integer RDW = 16 / (1 << READ_MODE)
+)(
+	// Write
+	input  wire [WAW-1:0] wr_addr,
+	input  wire [WDW-1:0] wr_data,
+	input  wire [WDW-1:0] wr_mask,
+	input  wire           wr_ena,
+	input  wire           wr_clk,
+
+	// Read
+	input  wire [RAW-1:0] rd_addr,
+	output wire [RDW-1:0] rd_data,
+	input  wire           rd_ena,
+	input  wire           rd_clk
+);
+
+	genvar i;
+
+	// Constants
+	// ---------
+
+	localparam integer WRITE_MODE_RAM = MASK_WORKAROUND ? 0 : WRITE_MODE;
+
+	localparam integer RDO = (1 << READ_MODE) >> 2;
+	localparam integer WDO = (1 << WRITE_MODE_RAM) >> 2;
+
+
+	// Functions
+	// ---------
+
+	function [15:0] bitrev16 (input [15:0] sig);
+		bitrev16 = {
+			sig[15], sig[7], sig[11], sig[3], sig[13], sig[5], sig[9], sig[1],
+			sig[14], sig[6], sig[10], sig[2], sig[12], sig[4], sig[8], sig[0]
+		};
+	endfunction
+
+
+	// Signals
+	// -------
+
+	// Raw RAM
+	wire [10:0] ram_wr_addr;
+	wire [15:0] ram_wr_data;
+	wire [15:0] ram_wr_mask;
+
+	wire [10:0] ram_rd_addr;
+	wire [15:0] ram_rd_data;
+
+
+	// Read mapping
+	// ------------
+
+	wire [15:0] rd_data_i;
+
+	assign { ram_rd_addr[7:0], ram_rd_addr[8], ram_rd_addr[9], ram_rd_addr[10] } = { rd_addr, {(3-READ_MODE){1'b0}} };
+
+	assign rd_data_i = bitrev16({ {RDO{1'b0}}, ram_rd_data[15:RDO] });
+	assign rd_data = rd_data_i[RDW-1:0];
+
+
+	// Write mapping
+	// -------------
+
+	generate
+		if ((WRITE_MODE == 0) | (MASK_WORKAROUND == 0) ) begin
+			// Normal Mapping rule
+			wire [15:0] wr_data_i = bitrev16({ {(16-WDW){1'b0}}, wr_data });
+			wire [15:0] wr_mask_i = bitrev16({ {(16-WDW){1'b0}}, wr_mask });
+
+			assign ram_wr_data = { wr_data_i[15-WDO:0], {WDO{1'b0}} };
+			assign ram_wr_mask = { wr_mask_i[15-WDO:0], {WDO{1'b0}} };
+			assign { ram_wr_addr[7:0], ram_wr_addr[8], ram_wr_addr[9], ram_wr_addr[10] } = { wr_addr, {(3-WRITE_MODE){1'b0}} };
+
+		end else begin
+			// We want mask support for non x16 mode
+			// To do this we have to stay in x16 mode and manually handle the
+			// write width adaptation
+			wire [15:0] submask;
+
+			assign ram_wr_data = bitrev16( {(1<<WRITE_MODE){wr_data}} );
+			assign ram_wr_mask = bitrev16( {(1<<WRITE_MODE){wr_mask}} | submask );
+			assign ram_wr_addr = { 3'b000, wr_addr[WAW-1:WAW-8] };
+
+			for (i=0; i<16; i=i+1)
+				assign submask[i] = !((i >> (4-WRITE_MODE)) == wr_addr[WRITE_MODE-1:0]);
+		end
+	endgenerate
+
+
+	// Memory block
+	// ------------
+
+	generate
+		if ((NEG_RD_CLK == 0) && (NEG_WR_CLK == 0))
+			SB_RAM40_4K #(
+				.INIT_FILE(INIT_FILE),
+				.WRITE_MODE(WRITE_MODE_RAM),
+				.READ_MODE(READ_MODE)
+			) ebr_I (
+				.RDATA(ram_rd_data),
+				.RADDR(ram_rd_addr),
+				.RCLK(rd_clk),
+				.RCLKE(rd_ena),
+				.RE(1'b1),
+				.WDATA(ram_wr_data),
+				.WADDR(ram_wr_addr),
+				.MASK(ram_wr_mask),
+				.WCLK(wr_clk),
+				.WCLKE(wr_ena),
+				.WE(1'b1)
+			);
+
+		else if ((NEG_RD_CLK != 0) && (NEG_WR_CLK == 0))
+			SB_RAM40_4KNR #(
+				.INIT_FILE(INIT_FILE),
+				.WRITE_MODE(WRITE_MODE_RAM),
+				.READ_MODE(READ_MODE)
+			) ebr_I (
+				.RDATA(ram_rd_data),
+				.RADDR(ram_rd_addr),
+				.RCLKN(rd_clk),
+				.RCLKE(rd_ena),
+				.RE(1'b1),
+				.WDATA(ram_wr_data),
+				.WADDR(ram_wr_addr),
+				.MASK(ram_wr_mask),
+				.WCLK(wr_clk),
+				.WCLKE(wr_ena),
+				.WE(1'b1)
+			);
+
+		else if ((NEG_RD_CLK == 0) && (NEG_WR_CLK != 0))
+			SB_RAM40_4KNW #(
+				.INIT_FILE(INIT_FILE),
+				.WRITE_MODE(WRITE_MODE_RAM),
+				.READ_MODE(READ_MODE)
+			) ebr_I (
+				.RDATA(ram_rd_data),
+				.RADDR(ram_rd_addr),
+				.RCLK(rd_clk),
+				.RCLKE(rd_ena),
+				.RE(1'b1),
+				.WDATA(ram_wr_data),
+				.WADDR(ram_wr_addr),
+				.MASK(ram_wr_mask),
+				.WCLKN(wr_clk),
+				.WCLKE(wr_ena),
+				.WE(1'b1)
+			);
+
+		else if ((NEG_RD_CLK != 0) && (NEG_WR_CLK != 0))
+			SB_RAM40_4KNRNW #(
+				.INIT_FILE(INIT_FILE),
+				.WRITE_MODE(WRITE_MODE_RAM),
+				.READ_MODE(READ_MODE)
+			) ebr_I (
+				.RDATA(ram_rd_data),
+				.RADDR(ram_rd_addr),
+				.RCLKN(rd_clk),
+				.RCLKE(rd_ena),
+				.RE(1'b1),
+				.WDATA(ram_wr_data),
+				.WADDR(ram_wr_addr),
+				.MASK(ram_wr_mask),
+				.WCLKN(wr_clk),
+				.WCLKE(wr_ena),
+				.WE(1'b1)
+			);
+
+	endgenerate
+
+endmodule

+ 172 - 0
cores/ice40/rtl/ice40_spram_gen.v

@@ -0,0 +1,172 @@
+/*
+ * ice40_spram_gen.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
+ * All rights reserved.
+ *
+ * BSD 3-clause, see LICENSE.bsd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+`default_nettype none
+
+module ice40_spram_gen #(
+	parameter integer ADDR_WIDTH = 15,
+	parameter integer DATA_WIDTH = 32,
+
+	// auto
+	parameter integer MASK_WIDTH = (DATA_WIDTH + 3) / 4,
+
+	parameter integer AL = ADDR_WIDTH - 1,
+	parameter integer DL = DATA_WIDTH - 1,
+	parameter integer ML = MASK_WIDTH - 1
+)(
+	input  wire [AL:0] addr,
+	output reg  [DL:0] rd_data,
+	input  wire        rd_ena,		// Write WILL corrupt read data
+	input  wire [DL:0] wr_data,
+	input  wire [ML:0] wr_mask,
+	input  wire        wr_ena,
+	input  wire        clk
+);
+
+	genvar x, y;
+
+	// Constants
+	// ---------
+
+	localparam integer ND = 1 << (ADDR_WIDTH - 14);
+	localparam integer NW = (DATA_WIDTH + 15) / 16;
+
+	localparam integer MSW = (ADDR_WIDTH > 14) ? (ADDR_WIDTH - 14) : 0;
+	localparam integer MDW = NW * 16;
+	localparam integer MMW = NW *  4;
+
+	initial
+		$display("ice40_spram_gen: (%dx%d) -> %dx%d array of SPRAM\n", (1<<ADDR_WIDTH), DATA_WIDTH, ND, NW);
+
+
+	// Signals
+	// -------
+
+	reg  [AL:0]    addr_r;
+
+	wire [13:0]    mem_addr;
+
+	wire [ND-1:0]  mem_sel;
+	wire [ND-1:0]  mem_ce;
+	wire [ND-1:0]  mem_wren;
+
+	wire [MDW-1:0] mem_do_m[0:ND-1];
+	wire [MDW-1:0] mem_do_w;
+	reg  [MDW-1:0] mem_di_w;
+	reg  [MMW-1:0] mem_dm_w;
+
+	wire [15:0]    mem_do[0:ND*NW-1];
+	wire [15:0]    mem_di[0:ND*NW-1];
+	wire [ 3:0]    mem_dm[0:ND*NW-1];
+
+
+	// Main logic
+	// ----------
+
+	// Register address for read-muxing
+	always @(posedge clk)
+		if (rd_ena)
+			addr_r <= addr;
+
+	// Address mapping
+	assign mem_addr = addr[13:0];
+
+	// Map input data to/from 16*NW bit words
+	always @(*)
+	begin : map
+		integer n, x, o;
+
+		// Map actual bits
+		for (n=0; n<MASK_WIDTH; n=n+1)
+		begin
+			// Determine position
+			x = n % NW;	// Which SPRAM
+			o = n / NW;	// Which nibble inside that SPRAM
+
+			// Map IO
+			mem_di_w[(16*x)+(4*o)+:4] =  wr_data[4*n+:4];
+			mem_dm_w[( 4*x)+   o    ] = ~wr_mask[n];
+
+			rd_data[4*n+:4] = mem_do_w[(16*x)+(4*o)+:4];
+		end
+	end
+
+	// Generate memory array
+	generate
+		// Per-depth loop
+		for (y=0; y<ND; y=y+1)
+		begin
+			// Per-width loop
+			for (x=0; x<NW; x=x+1)
+			begin
+				// IO mapping for word
+				assign mem_di[y*NW+x] = mem_di_w[x*16+:16];
+				assign mem_dm[y*NW+x] = mem_dm_w[x* 4+: 4];
+
+				assign mem_do_m[y][x*16+:16] = mem_do[y*NW+x];
+
+				// Memory element
+				SB_SPRAM256KA ram_I (
+					.ADDRESS   (mem_addr),
+					.DATAIN    (mem_di[y*NW+x]),
+					.MASKWREN  (mem_dm[y*NW+x]),
+					.WREN      (mem_wren[y]),
+					.CHIPSELECT(mem_ce[y]),
+					.CLOCK     (clk),
+					.STANDBY   (1'b0),
+					.SLEEP     (1'b0),
+					.POWEROFF  (1'b1),
+					.DATAOUT   (mem_do[y*NW+x])
+				);
+			end
+
+			// Enables
+			if (MSW == 0)
+				assign mem_sel[y] = 1'b1;
+			else
+				assign mem_sel[y] = (addr[AL:AL-MSW+1] == y);
+
+			assign mem_wren[y] = mem_sel[y] &  wr_ena;
+			assign mem_ce[y]   = mem_sel[y] & (wr_ena | rd_ena);
+
+			// Muxing
+			if (MSW == 0)
+				// Trivial case
+				assign mem_do_w = mem_do_m[0];
+			else
+				// Read side mux
+				assign mem_do_w = mem_do_m[addr_r[AL:AL-MSW+1]];
+		end
+	endgenerate
+
+endmodule

+ 138 - 0
cores/ice40/sim/ice40_ebr_tb.v

@@ -0,0 +1,138 @@
+/*
+ * ice40_ebr_tb.v
+ *
+ * vim: ts=4 sw=4
+ *
+ * Copyright (C) 2020  Sylvain Munaut <tnt@246tNt.com>
+ * All rights reserved.
+ *
+ * BSD 3-clause, see LICENSE.bsd
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of the <organization> nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+`default_nettype none
+`timescale 1ns/100ps
+
+module ice40_ebr_tb #(
+	parameter integer READ_MODE  = 2,	/* 0 =  256x16, 1 =  512x8 */
+	parameter integer WRITE_MODE = 1,	/* 2 = 1024x4,  3 = 2048x2 */
+	parameter integer MASK_WORKAROUND = 1
+);
+
+	// Config
+	// ------
+
+	localparam integer WAW = 8 + WRITE_MODE;
+	localparam integer WDW = 16 / (1 << WRITE_MODE);
+	localparam integer RAW = 8 + READ_MODE;
+	localparam integer RDW = 16 / (1 << READ_MODE);
+
+
+	// Signals
+	// -------
+
+	reg  [WAW-1:0] wr_addr;
+	reg  [WDW-1:0] wr_data;
+	reg  [WDW-1:0] wr_mask;
+	reg            wr_ena;
+
+	reg  [RAW-1:0] rd_addr;
+	wire [RDW-1:0] rd_data;
+	reg            rd_ena;
+
+	reg  clk = 0;
+	reg  rst = 1;
+
+
+	// Test bench
+	// ----------
+
+	// Setup recording
+	initial begin
+		$dumpfile("ice40_ebr_tb.vcd");
+		$dumpvars(0,ice40_ebr_tb);
+	end
+
+	// Reset pulse
+	initial begin
+		# 200 rst = 0;
+		# 1000000 $finish;
+	end
+
+    // Clocks
+    always #10 clk = !clk;
+
+
+
+	// DUT
+	// ---
+
+	ice40_ebr #(
+		.READ_MODE(READ_MODE),
+		.WRITE_MODE(WRITE_MODE),
+		.MASK_WORKAROUND(MASK_WORKAROUND)
+	) dut_I (
+		.wr_addr(wr_addr),
+		.wr_data(wr_data),
+		.wr_mask(wr_mask),
+		.wr_ena(wr_ena),
+		.wr_clk(clk),
+		.rd_addr(rd_addr),
+		.rd_data(rd_data),
+		.rd_ena(rd_ena),
+		.rd_clk(clk)
+	);
+
+
+	reg mode;
+
+	always @(posedge clk)
+	begin
+		// Writes
+		if (!mode) begin
+			wr_addr <= wr_addr + wr_ena;
+			wr_data <= $random;
+			wr_mask <= 8'hf0;
+			wr_ena  <= ~&wr_addr;
+			mode    <= mode ^ &wr_addr;
+		end
+
+		// Reads
+		if (mode) begin
+			rd_addr <= rd_addr + rd_ena;
+			rd_ena  <= ~&rd_addr;
+			mode    <= mode ^ &rd_addr;
+		end
+
+		// Reset
+		if (rst) begin
+			wr_addr <= 0;
+			wr_ena  <= 1'b0;
+			rd_addr <= 0;
+			rd_ena  <= 1'b0;
+			mode    <= 1'b0;
+		end
+	end
+
+endmodule