Commit Diff


commit - /dev/null
commit + 8515162d03a7aafa6b9f4d109253d9b38575414a
blob - /dev/null
blob + 5d210ac7e7e5bba96d91669f9159c1fc57d61644 (mode 644)
--- /dev/null
+++ .gitignore
@@ -0,0 +1,4 @@
+ulx3s.bit
+ulx3s.json
+ulx3s_out.config
+design.json
blob - /dev/null
blob + 4d394a82bcc29895e1da9f1c69082c9f5c048a9a (mode 644)
--- /dev/null
+++ Makefile
@@ -0,0 +1,34 @@
+.POSIX:
+
+CLOCKS = src/clk1.v
+SRC = ${CLOCKS} src/sdram.v src/cpu.v src/top.v
+
+all: ulx3s.bit
+
+clean:
+	rm -f ${CLOCKS} ulx3s.bit ulx3s_out.config design.json
+
+flash: ulx3s.bit
+	fujprog ulx3s.bit
+
+flash-rom: ulx3s.bit
+	fujprog -j FLASH ulx3s.bit
+
+ulx3s.bit: ulx3s_out.config
+	ecppack ulx3s_out.config ulx3s.bit
+
+ulx3s_out.config: design.json ulx3s_v20.lpf
+	nextpnr-ecp5 	--quiet			\
+			--top=top		\
+			--85k			\
+			--json design.json	\
+			--package CABGA381	\
+			--lpf ulx3s_v20.lpf	\
+			--textcfg ulx3s_out.config
+
+design.json: ${SRC}
+	yosys -q -p 'synth_ecp5 -noccu2 -nomux -json design.json' ${SRC}
+
+src/clk1.v:
+	ecppll -i 25 -o 40 -n Clock1 -f $@
+
blob - /dev/null
blob + bbe26aa689f251fccea099b9b670fbd80eb37af7 (mode 644)
--- /dev/null
+++ isa
@@ -0,0 +1,55 @@
+# Registers
+
+16 bit
+8 GPRs (zero=0, sp=6, lr=7)
+1 PC
+
+# Instructions
+
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| rep                 | instr               | func                          | F | descr                                      |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| 0000 0RRR 0RRR 0RRR | add rd, rs, rt      | rd = rs + rt                  | R | Addition                                   |
+| 0000 0RRR 0RRR 1RRR | sub rd, rs, rt      | rd = rs - rt                  | R | Subtraction                                |
+| 0000 0RRR 1RRR 0RRR | and rd, rs, rt      | rd = rs & rt                  | R | Logical AND                                |
+| 0000 0RRR 1RRR 1RRR | or rd, rs, rt       | rd = rs or rt                 | R | Logical OR                                 |
+| 0000 1RRR 0RRR 0RRR | lsh rd, rs, rt      | rd = lshift(rs, rt)           | R | Logical bi-directional shift               |
+| 0000 1RRR 0RRR 1RRR | xor rd, rs, rt      | rd = rs ^ rt                  | R | Logical XOR                                |
+| 0000 1RRR 1RRR 0RRR | slt rd, rs, rt      | rd = rs < rt  (signed)        | R | Set if less than (signed)                  |
+| 0000 1RRR 1RRR 1RRR | sltu rd, rs, rt     | rd = rs < rt (unsigned)       | R | Set if less than (unsigned)                |
+| 0001 0RRR 0RRR 0RRR | jalr rd, rt         | rd = pc; pc = rt              | R | Jump to Register and Link                  |
+| 0001 0RRR 1RRR 0RRR | ud                  |                               | R | (undefined)                                |
+| 0001 1RRR 0RRR 0RRR | ud                  |                               | R |                                            |
+| 0001 1RRR 1RRR 0RRR | ud                  |                               | R |                                            |
+| 0001 xRRR xRRR 1RRR | ud                  |                               | R |                                            |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| 0010 0RRR IIII IIII | addi reg, simm8     | rd = rd + simm8               | I | Add w/ immediate                           |
+| 0010 1RRR IIII IIII | lshi reg, simm8     | rd = lshift(rd, simm8)        | I | Logical bi-directional shift w/ immediate  |
+| 0011 0RRR IIII IIII | lui rd, imm8        | rd = (imm8 << 8)              | I | Load upper-immediate                       |
+| 0011 1RRR IIII IIII | li rd, imm8         | rd = imm8                     | I | Load lower-immediate                       |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| 0100 IRRR IIII IRRR | lw rd, [rt, simm6]  | rd = *(rt + simm6)            | L | Load word from memory                      |
+| 0101 IRRR IIII IRRR | lbu rd, [rt, simm6] | rd = *(rt + simm6)            | L | Load unsigned byte from memory (TODO)      |
+| 0110 IRRR IIII IRRR | lb rd, [rt, simm6]  | rd = *(rt + simm6)            | L | Load sign-extended byte from memory (TODO) |
+| 0111 IRRR IIII IRRR | ud                  |                               | L |                                            |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| 1000 IIII IRRR IRRR | sw rs, [rt, simm6]  | *(rt + simm6 * 2) = rs        | S | Store word to memory                       |
+| 1001 IIII IRRR IRRR | sb rs, [rt, simm6]  | *(rt + simm6) = rs            | S | Store byte to memory (TODO)                |
+| 1010 IIII IRRR IRRR | beq rs, rt, simm6   | if (rs == rt) pc += simm6 * 2 | S | PC-relative branch if equal                |
+| 1011 IIII IRRR IRRR | bne rs, rt, simm6   | if (rs != rt) pc += simm6 * 2 | S | PC-relative branch if not equal            |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| 110x xxxx xxxx xxxx | ud                  |                               | ? |                                            |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+| 1110 IIII IIII IIII | jal simm12          | lr = pc; pc += simm12 * 2     | J | PC-relative Jump and Link                  |
+| 1111 IIII IIII IIII | j simm12            | pc += simm12 * 2              | J | PC-relative Jump                           |
+|---------------------|---------------------|-------------------------------|---|--------------------------------------------|
+
+```c
+uint16_t lshift(uint16_t a, int16_t b) {
+	if (b < 0) {
+		return a >> -b;
+	} else {
+		return a << b;
+	}
+}
+```
blob - /dev/null
blob + 9249f3fc3e890c2c9c6c0e3788fc2773bbd6a23f (mode 644)
--- /dev/null
+++ src/clk1.v
@@ -0,0 +1,46 @@
+// diamond 3.7 accepts this PLL
+// diamond 3.8-3.9 is untested
+// diamond 3.10 or higher is likely to abort with error about unable to use feedback signal
+// cause of this could be from wrong CPHASE/FPHASE parameters
+module Clock1
+(
+    input clkin, // 25 MHz, 0 deg
+    output clkout0, // 40 MHz, 0 deg
+    output locked
+);
+(* FREQUENCY_PIN_CLKI="25" *)
+(* FREQUENCY_PIN_CLKOP="40" *)
+(* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
+EHXPLLL #(
+        .PLLRST_ENA("DISABLED"),
+        .INTFB_WAKE("DISABLED"),
+        .STDBY_ENABLE("DISABLED"),
+        .DPHASE_SOURCE("DISABLED"),
+        .OUTDIVIDER_MUXA("DIVA"),
+        .OUTDIVIDER_MUXB("DIVB"),
+        .OUTDIVIDER_MUXC("DIVC"),
+        .OUTDIVIDER_MUXD("DIVD"),
+        .CLKI_DIV(5),
+        .CLKOP_ENABLE("ENABLED"),
+        .CLKOP_DIV(15),
+        .CLKOP_CPHASE(7),
+        .CLKOP_FPHASE(0),
+        .FEEDBK_PATH("CLKOP"),
+        .CLKFB_DIV(8)
+    ) pll_i (
+        .RST(1'b0),
+        .STDBY(1'b0),
+        .CLKI(clkin),
+        .CLKOP(clkout0),
+        .CLKFB(clkout0),
+        .CLKINTFB(),
+        .PHASESEL0(1'b0),
+        .PHASESEL1(1'b0),
+        .PHASEDIR(1'b1),
+        .PHASESTEP(1'b1),
+        .PHASELOADREG(1'b1),
+        .PLLWAKESYNC(1'b0),
+        .ENCLKOP(1'b0),
+        .LOCK(locked)
+	);
+endmodule
blob - /dev/null
blob + a904ee1746137195fafe16dad4a887efac2510b0 (mode 644)
--- /dev/null
+++ src/cpu-old.v
@@ -0,0 +1,278 @@
+// Control Word
+`define BIT_MEM_EN	0
+`define BIT_PC_INC	1
+`define BIT_ACC_WR	2
+`define BIT_ACC_EN	3
+`define BIT_LOAD_INSTR	4
+`define BIT_MAR_WR	5
+`define BIT_REG_WR	6
+`define BIT_ALU_EN	7
+`define BIT_PC_EN	8
+`define BIT_MAR_EN	9
+`define BIT_MEM_WR	10
+`define BIT_PC_WR	11
+`define BIT_FLAGS_WR	12
+`define BIT_TIMER_RST	14
+`define BIT_HLT		15
+
+// Flags
+`define BIT_ZERO	0
+`define BIT_CARRY	1
+
+`define MEM_EN		(16'b1 << `BIT_MEM_EN)
+`define PC_INC		(16'b1 << `BIT_PC_INC)
+`define ACC_WR		(16'b1 << `BIT_ACC_WR)
+`define ACC_EN		(16'b1 << `BIT_ACC_EN)
+`define MAR_WR		(16'b1 << `BIT_MAR_WR)
+`define LOAD_INSTR	(16'b1 << `BIT_LOAD_INSTR)
+`define REG_WR		(16'b1 << `BIT_REG_WR)
+`define ALU_EN		(16'b1 << `BIT_ALU_EN)
+`define PC_EN		(16'b1 << `BIT_PC_EN)
+`define MAR_EN		(16'b1 << `BIT_MAR_EN)
+`define MEM_WR		(16'b1 << `BIT_MEM_WR)
+`define PC_WR		(16'b1 << `BIT_PC_WR)
+`define FLAGS_WR	(16'b1 << `BIT_FLAGS_WR)
+`define TIMER_RST	(16'b1 << `BIT_TIMER_RST)
+`define HLT		(16'b1 << `BIT_HLT)
+
+`define ZERO		(4'b1 << `BIT_ZERO)
+`define CARRY		(4'b1 << `BIT_CARRY)
+
+module ProgramCounter(
+		      inout [11:0]	addr,
+		      input		inc,
+		      input		en,
+		      input		wr,
+		      input		clk,
+		      input		rst
+		      );
+
+   reg [11:0] value = 0;
+  
+  always@ (posedge clk or posedge rst)
+    begin
+       if (rst)
+	 value <= 0;
+       else if (wr && !en)
+	 value <= addr;
+       else if (inc)
+	 value <= value + 1;
+    end
+
+   assign addr = en && !wr ? value : 12'hzzz;
+  
+endmodule // ProgramCounter
+
+module Register(
+		output reg [7:0] data_out = 0,
+		input [7:0]	 data_in,
+		input		 wr,
+		input		 clk,
+		input		 rst
+		);
+
+always@ (posedge clk or posedge rst)
+  begin
+     if (rst)
+       data_out <= 0;
+     else if (wr)
+       data_out <= data_in;
+  end
+
+endmodule // Register
+
+module Accumulator(
+		   inout [7:0]	bus,
+		   output [7:0]	out,
+		   input	clk,
+		   input	rst,
+		   input	wr,
+		   input	en
+		   );
+
+   reg [7:0] value = 0;
+
+
+   always@ (posedge clk or posedge rst)
+     begin
+	if (rst)
+	  value <= 0;
+	else if (wr && !en)
+	  value <= bus;
+     end
+
+   assign out = value;
+   assign bus = (!wr && en) ? value : 8'hzz;
+endmodule // Accumulator
+
+module InstructionDecoder(
+			  input [3:0]		  timer,
+			  input [7:0]		  instr,
+			  input			  clk,
+			  output reg [`BIT_HLT:0] control_word
+			  );
+   wire [`BIT_HLT:0] cw_instr [15:0] [15:2];
+
+   // hlt
+   assign cw_instr[4'b0000][4'b0010] = `HLT;
+
+   // lda imm8
+   assign cw_instr[4'b0001][4'b0010] = `PC_EN | `MEM_EN | `ACC_WR;	
+   assign cw_instr[4'b0001][4'b0011] = `PC_INC | `TIMER_RST;
+
+   // alu imm8
+   assign cw_instr[4'b0010][4'b0010] = `PC_EN | `MEM_EN | `REG_WR;
+   assign cw_instr[4'b0010][4'b0011] = `PC_INC | `ALU_EN | `ACC_WR | `TIMER_RST;
+
+   // rb imm12
+   assign cw_instr[4'b0011][4'b0010] = `PC_EN | `MEM_EN | `MAR_WR;
+   assign cw_instr[4'b0011][4'b0011] = `PC_INC | `MAR_EN | `MEM_EN | `ACC_WR | `TIMER_RST;
+
+   // wb imm12
+   assign cw_instr[4'b0100][4'b0010] = `PC_EN | `MEM_EN | `MAR_WR;
+   assign cw_instr[4'b0100][4'b0011] = `PC_INC | `MAR_EN | `MEM_WR | `ACC_EN | `TIMER_RST;
+
+   // jmp imm12
+   assign cw_instr[4'b0101][4'b0010] = `PC_EN | `MEM_EN | `MAR_WR;
+   assign cw_instr[4'b0101][4'b0011] = `MAR_EN | `PC_WR | `TIMER_RST;
+
+   always@ (negedge clk)
+      case (timer)
+	4'b0000: control_word <= `PC_EN | `MEM_EN | `LOAD_INSTR;
+	4'b0001: control_word <= `PC_INC;
+	default: control_word <= cw_instr[instr[7:4]][timer];
+      endcase
+
+endmodule // InstructionDecoder
+
+module ALU(
+	   input [7:0]	in_a,
+	   input [7:0]	in_b,
+	   input [3:0]	funct,
+	   input [3:0]	flags_in,
+	   output [7:0]	out,
+	   output [3:0]	flags_out,
+	   input	en
+	   );
+   reg [8:0] result;
+
+   always@ (*)
+     begin
+	case (funct)
+	  4'b0000: result = in_a + in_b;
+	  4'b0001: result = in_a - in_b;
+	  4'b0010: result = { 1'b0, in_a & in_b };
+	  4'b0011: result = { 1'b0, in_a | in_b };
+	  4'b0100: result = { 1'b0, in_a ^ in_b };
+	  4'b0101: result = { 1'b0, in_a << in_b };
+	  4'b0110: result = { 1'b0, in_a >> in_b };
+	  4'b0111: result = { 1'b0, in_a >>> in_b };
+	  default: result = 9'bx;
+	endcase
+     end
+
+   assign flags_out[`BIT_ZERO] = result[7:0] == 0;
+   assign flags_out[`BIT_CARRY] = result[8];
+   assign out = en ? result[7:0] : 8'bz;
+endmodule // ALU
+
+module CPU(
+	   output [11:0] addr_out,
+	   inout [7:0]	 bus,
+	   output [7:0]	 debug,
+	   output	 mem_wr,
+	   output	 mem_en,
+	   output reg	 hlt = 0,
+	   input	 clk,
+	   input	 rst
+	   );
+   wire [`BIT_HLT:0] control_word;
+   wire [7:0]	     instr;
+   wire [7:0]	     alu_in_a;
+   wire [7:0]	     alu_in_b;
+   wire [3:0]	     flags_alu;
+   reg [3:0]	     timer = 0;
+   reg [11:0]	     mar;
+   reg [3:0]	     flags;
+
+   assign debug = instr;
+
+   ProgramCounter _pc(
+		      .addr(addr_out),
+		      .inc(control_word[`BIT_PC_INC]),
+		      .en(control_word[`BIT_PC_EN]),
+		      .wr(control_word[`BIT_PC_WR]),
+		      .clk(clk),
+		      .rst(rst)
+		      );
+
+   Register _ireg(
+		  .data_in(bus),
+		  .data_out(instr),
+		  .wr(control_word[`BIT_LOAD_INSTR]),
+		  .clk(clk),
+		  .rst(rst)
+		  );
+
+   InstructionDecoder _idec(
+			    .instr(instr),
+			    .clk(clk),
+			    .timer(timer),
+			    .control_word(control_word)
+			    );
+
+   Accumulator _acc(
+		    .bus(bus),
+		    .out(alu_in_a),
+		    .clk(clk),
+		    .rst(rst),
+		    .wr(control_word[`BIT_ACC_WR]),
+		    .en(control_word[`BIT_ACC_EN])
+		    );
+
+   Register _reg(
+		 .data_in(bus),
+		 .data_out(alu_in_b),
+		 .clk(clk),
+		 .rst(rst),
+		 .wr(control_word[`BIT_REG_WR])
+		 );
+   
+   ALU _alu(
+	    .in_a(alu_in_a),
+	    .in_b(alu_in_b),
+	    .funct(instr[3:0]),
+	    .out(bus),
+	    .flags_in(flags),
+	    .flags_out(flags_alu),
+	    .en(control_word[`BIT_ALU_EN])
+	    );
+
+   assign addr_out = control_word[`BIT_MAR_EN] ? mar : 12'hzzz;
+   assign mem_wr = control_word[`BIT_MEM_WR];
+   assign mem_en = control_word[`BIT_MEM_EN];
+
+   always@ (posedge clk or posedge rst)
+     if (rst)
+      flags <= 0;
+    else if (control_word[`BIT_FLAGS_WR])
+      flags <= flags_alu;
+
+   always@ (posedge clk or posedge rst)
+     if (rst)
+       hlt <= 0;
+     else if (control_word[`BIT_HLT])
+       hlt <= 1;
+
+   always@ (posedge clk)
+     if (control_word[`BIT_MAR_WR])
+       mar <= { instr[3:0], bus };
+
+   always@ (posedge clk or posedge rst)
+     if (rst)
+       timer <= 0;
+     else if (control_word[`BIT_TIMER_RST])
+       timer <= 0;
+     else
+       timer <= timer + 1;
+endmodule // CPU
blob - /dev/null
blob + d1e8028ade1f8c557cd08429fc3281279511c971 (mode 644)
--- /dev/null
+++ src/cpu.v
@@ -0,0 +1,234 @@
+`define FMT_R		3'b000
+`define FMT_I		3'b001
+`define FMT_L		3'b010
+`define FMT_S		3'b011
+`define FMT_J		3'b100
+`define FMT_UD		3'b111
+
+`define CWB_MEM_WR	0
+`define CWB_MEM_EN	1
+`define CWB_PC_WR	2
+`define CWB_IR_WR	3
+`define CWB_WB		4
+
+`define CW_MEM_WR	(16'b1 << `CWB_MEM_WR)
+`define CW_MEM_EN	(16'b1 << `CWB_MEM_EN)
+`define CW_PC_WR	(16'b1 << `CWB_PC_WR)
+`define CW_IR_WR	(16'b1 << `CWB_IR_WR)
+`define CW_WB		(16'b1 << `CWB_WB)
+
+function [15:0] lshift(input [15:0] value, input [15:0] shamt);
+   if (shamt[15] == 1'b0)
+     lshift = value << shamt;
+   else
+     lshift = value >> -shamt;
+endfunction
+
+module ALU(
+	   input [15:0]	     in1,
+	   input [15:0]	     in2,
+	   input [2:0]	     op,
+	   output reg [15:0] out
+	   );
+
+   wire signed [15:0] in1s = in1;
+   wire signed [15:0] in2s = in2;
+
+   always@ (*) begin
+      case (op)
+	3'b000: out = in1 + in2;
+	3'b001: out = in1 - in2;
+	3'b010: out = in1 & in2;
+	3'b011: out = in1 | in2;
+	3'b100: out = lshift(in1, in2);
+	3'b101: out = in1 ^ in2;
+	3'b110: out = { 15'b0, (in1 < in2) };
+	3'b111: out = { 15'b0, (in1s < in2s) };
+      endcase
+   end
+endmodule
+
+module Processor(
+		 output [7:0]	   debug,
+		 output reg [15:0] mem_addr,
+		 inout [15:0]	   mem_data,
+		 output		   mem_wr,
+		 output		   mem_en,
+		 input		   mem_busy,
+		 input		   clk,
+		 input		   rst
+		 );
+
+   wire [15:0] alu_in2, alu_out;
+   reg [15:0]  regs [7:1];
+   reg [15:0]  rd, pc, next_pc, instr;
+   reg [15:0]  control_word;
+   reg [3:0]   state = 0;
+   reg [2:0]   ifmt;
+   reg [15:0]  imm, imm2;
+   wire [2:0]  rd_sel = instr[15:12] != 4'b1110 ? instr[10:8] : 3'h7;
+   wire [2:0]  rs_sel = ifmt == `FMT_I ? instr[10:8] : instr[6:4];
+   wire [2:0]  rt_sel = instr[2:0];
+   wire [2:0]  alu_op = ifmt == `FMT_R ? { instr[11], instr[7], instr[3] } : { instr[11], 2'b00 };
+   wire [15:0] rs = regs[rs_sel];
+   wire [15:0] rt = regs[rt_sel];
+   wire [15:0] alu_in1 = rs;
+
+   assign debug = control_word[7:0];
+   
+   ALU _alu(
+	    .in1(alu_in1),
+	    .in2(alu_in2),
+	    .op(alu_op),
+	    .out(alu_out)
+	    );
+
+   always@ (posedge clk or posedge rst) begin
+      if (rst)
+	pc <= 16'b0;
+      else if (control_word[`CWB_PC_WR]	)
+	pc <= next_pc;
+   end
+
+   always@ (posedge clk or posedge rst) begin
+      if (rst)
+	instr <= 0;
+      else if (control_word[`CWB_IR_WR])
+	instr <= mem_data;
+   end
+
+   always@ (negedge clk or posedge rst) begin
+      if (rst)
+	state <= 0;
+      else begin
+	 if (mem_busy)
+	   control_word <= 0;
+	 else begin
+	    case (state)
+	      // Fetch
+	      4'b0000: begin
+		 mem_addr <= pc;
+		 next_pc <= pc + 2;
+		 control_word <= `CW_MEM_EN | `CW_IR_WR | `CW_PC_WR;
+		 state <= state + 1;
+	      end
+	      // Execute
+	      4'b0001: begin
+		 casez (instr[15:12])
+		   // alu rd, rs, rt
+		   4'b0000: begin
+		      rd <= alu_out;
+		      control_word <= `CW_WB;
+		      state <= 0;
+		   end
+		   // jalr rd, rt
+		   4'b0001: begin
+		      next_pc <= rt;
+		      rd <= pc;
+		      control_word <= `CW_WB | `CW_PC_WR;
+		      state <= 0;
+		   end
+		   // alui rd, imm
+		   4'b0010: begin
+		      rd <= alu_out;
+		      control_word <= `CW_WB;
+		      state <= 0;
+		   end
+		   // lui/li rd, imm
+		   4'b0011: begin
+		      rd <= instr[11] ? imm : (imm << 8);
+		      control_word <= `CW_WB;
+		      state <= 0;
+		   end
+		   // lw rd, [rt, imm]
+		   4'b0100: begin
+		      mem_addr <= rt;
+		      control_word <= `CW_MEM_EN;
+		      state <= state + 1;
+		   end
+		   // TODO: lb, lbu
+		   // sw rs, [rt, imm]
+		   4'b1000: begin
+		      mem_addr <= rt;
+		      control_word <= `CW_MEM_WR;
+		      state <= 0;
+		   end
+		   // TODO: sb
+		   // bcc rs, rt, imm
+		   4'b101?: begin
+		      if ((rs == rt) ^ instr[12]) begin
+			 next_pc <= pc + imm;
+			 control_word <= `CW_PC_WR;
+		      end
+		      else control_word <= 0;
+		      state <= 0;
+		   end
+		   // jal imm
+		   4'b1110: begin
+		      rd <= pc;
+		      next_pc <= pc + imm;
+		      control_word <= `CW_WB | `CW_PC_WR;
+		      state <= 0;
+		   end
+		   // j imm
+		   4'b1111: begin
+		      next_pc <= pc + imm;
+		      control_word <= `CW_PC_WR;
+		      state <= 0;
+		   end
+		 endcase
+	      end // case: 4'b0001
+	      // WB Memory
+	      4'b0010: begin
+		 case (instr[15:12])
+		   4'b0100: begin
+		      rd <= mem_data;
+		      control_word <= `CW_WB;
+		      state <= 0;
+		   end
+		   default: state <= 0;
+		 endcase
+	      end
+	      default: state <= 0;
+	    endcase
+	 end
+      end
+   end
+
+   always@ (posedge clk) begin
+      if (control_word[`CWB_WB] && rd_sel != 0)
+	regs[rd_sel] <= rd;
+   end
+
+   always@ (*) begin
+      casez (instr[15:12])
+	4'b000?: ifmt = `FMT_R;
+	4'b001?: ifmt = `FMT_I;
+	4'b01??: ifmt = `FMT_L;
+	4'b10??: ifmt = `FMT_S;
+	4'b110?: ifmt = `FMT_UD;
+	4'b111?: ifmt = `FMT_J;
+      endcase
+   end
+
+   always@ (*) begin
+      case (ifmt)
+	`FMT_I: imm2 = { {8{instr[7]}}, instr[7:0] };
+	`FMT_L: imm2 = { {10{instr[11]}}, instr[11], instr[7:3] };
+	`FMT_S: imm2 = { {10{instr[11]}}, instr[11:7], instr[3] };
+	`FMT_J: imm2 = { {4{instr[11]}}, instr[11:0] };
+	default: imm2 = 0;
+      endcase
+      casez (instr[15:12])
+	4'b1000: imm = imm2 << 1;
+	4'b101?: imm = imm2 << 1;
+	4'b111?: imm = imm2 << 1;
+	default: imm = imm2;
+      endcase
+   end
+
+   assign alu_in2 = ifmt == `FMT_R ? rt : imm;
+   assign mem_wr = control_word[`CWB_MEM_WR];
+   assign mem_en = control_word[`CWB_MEM_EN];
+   assign mem_data = control_word[`CWB_MEM_WR] ? rs : 16'bz;
+endmodule
blob - /dev/null
blob + 72565f08739c1c53fbdcfc0a29560fc275496882 (mode 644)
--- /dev/null
+++ src/sdram.v
@@ -0,0 +1,330 @@
+
+// SDRAM interface to AS4C32M16SB-7TCN
+// 512 Mbit Single-Data-Rate SDRAM, 32Mx16 (8M x 16 x 4 Banks)
+
+// Matthias Koch, January 2022
+
+// With a lot of inspiration from Mike Field, Hamsterworks:
+
+// https://web.archive.org/web/20190215130043/http://hamsterworks.co.nz/mediawiki/index.php/Simple_SDRAM_Controller
+// https://web.archive.org/web/20190215130043/http://hamsterworks.co.nz/mediawiki/index.php/File:Verilog_Memory_controller_v0.1.zip
+
+// Note: You may need to change all values marked with *** when changing clock frequency. This is for 40 MHz.
+
+module SDRAM (
+
+  // Interface to SDRAM chip, fully registered
+
+  output             sd_clk,        // Clock for SDRAM chip
+  output reg         sd_cke,        // Clock enabled
+  inout      [15:0]  sd_d,          // Bidirectional data lines to/from SDRAM
+  output reg [12:0]  sd_addr,       // Address bus, multiplexed, 13 bits
+  output reg  [1:0]  sd_ba,         // Bank select wires for 4 banks
+  output reg  [1:0]  sd_dqm,        // Byte mask
+  output reg         sd_cs,         // Chip select
+  output reg         sd_we,         // Write enable
+  output reg         sd_ras,        // Row address select
+  output reg         sd_cas,        // Columns address select
+
+  // Interface to processor
+
+  input  clk,
+  input  resetn,
+  input  [3:0] wmask,
+  input  rd,
+  input  [25:0] addr,
+  input  [31:0] din,
+  output reg [31:0] dout,
+  output reg busy
+);
+
+  parameter sdram_startup_cycles = 10100; // *** -- 100us, plus a little more, @ 100MHz
+  parameter sdram_refresh_cycles = 195;  // *** The refresh operation must be performed 8192 times within 64ms. --> One refresh every 7.8125 us.
+                                        // With a minimum clock of 25 MHz, this results in one refresh every 7.8125e-6 * 25e6 = 195 cycles.
+
+  // ----------------------------------------------------------
+  // -- Connections and buffer primitives
+  // ----------------------------------------------------------
+
+  assign sd_clk = ~clk;   // Supply memory chip with a clock.
+
+  wire [15:0] sd_data_in;     // Bidirectional data from SDRAM
+  reg  [15:0] sd_data_out;    // Bidirectional data to   SDRAM
+  reg         sd_data_drive;  // High: FPGA controls wires Low: SDRAM controls wires
+
+
+  `ifdef __ICARUS__
+
+  reg [15:0] sd_data_in_buffered;
+  assign sd_d = sd_data_drive ? sd_data_out : 16'bz;
+  always @(posedge clk) sd_data_in_buffered <= sd_d;
+  assign sd_data_in = sd_data_in_buffered;
+
+  `else
+
+  wire [15:0] sd_data_in_unbuffered;  // To connect primitives internally
+
+  TRELLIS_IO #(.DIR("BIDIR"))
+  sdio_tristate[15:0] (
+    .B(sd_d),
+    .I(sd_data_out),
+    .O(sd_data_in_unbuffered),
+    .T(!sd_data_drive)
+  );
+
+  // Registering the input is important for stability and delays data arrival by one clock cycle.
+  IFS1P3BX dbi_ff[15:0] (.D(sd_data_in_unbuffered), .Q(sd_data_in), .SCLK(clk),  .PD({16{sd_data_drive}}));
+
+  `endif
+  // ----------------------------------------------------------
+  // -- Configuration to initialise the SDRAM chip
+  // ----------------------------------------------------------
+
+  // Taken from https://github.com/rxrbln/picorv32/blob/master/picosoc/sdram.v
+
+  localparam NO_WRITE_BURST = 1'b0;   // 0=write burst enabled, 1=only single access write
+  localparam OP_MODE        = 2'b00;  // only 00 (standard operation) allowed
+  localparam CAS_LATENCY    = 3'd2;   // 2 or 3 cycles allowed
+  localparam ACCESS_TYPE    = 1'b0;   // 0=sequential, 1=interleaved
+  localparam BURST_LENGTH   = 3'b001; // 000=1, 001=2, 010=4, 011=8
+
+  localparam MODE = {3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
+
+  // ----------------------------------------------------------
+  // -- All possible commands for the SDRAM chip
+  // ----------------------------------------------------------
+
+  //                           CS, RAS, CAS, WE
+  localparam CMD_INHIBIT         = 4'b1111;
+
+  localparam CMD_NOP             = 4'b0111;
+  localparam CMD_BURST_TERMINATE = 4'b0110;
+  localparam CMD_READ            = 4'b0101;
+  localparam CMD_WRITE           = 4'b0100;
+  localparam CMD_ACTIVE          = 4'b0011;
+  localparam CMD_PRECHARGE       = 4'b0010;
+  localparam CMD_AUTO_REFRESH    = 4'b0001;
+  localparam CMD_LOAD_MODE       = 4'b0000;
+
+  // ----------------------------------------------------------
+  // -- States of the SDRAM controller
+  // ----------------------------------------------------------
+
+  localparam s_init_bit      = 0;  localparam s_init      = 1 << s_init_bit      ;
+  localparam s_idle_bit      = 1;  localparam s_idle      = 1 << s_idle_bit      ;
+  localparam s_activate_bit  = 2;  localparam s_activate  = 1 << s_activate_bit  ;
+  localparam s_read_1_bit    = 3;  localparam s_read_1    = 1 << s_read_1_bit    ;
+  localparam s_read_2_bit    = 4;  localparam s_read_2    = 1 << s_read_2_bit    ;
+  localparam s_read_3_bit    = 5;  localparam s_read_3    = 1 << s_read_3_bit    ;
+  localparam s_read_4_bit    = 6;  localparam s_read_4    = 1 << s_read_4_bit    ;
+  localparam s_read_5_bit    = 7;  localparam s_read_5    = 1 << s_read_5_bit    ;
+  localparam s_write_1_bit   = 8;  localparam s_write_1   = 1 << s_write_1_bit   ;
+  localparam s_write_2_bit   = 9;  localparam s_write_2   = 1 << s_write_2_bit   ;
+
+  localparam s_idle_in_6_bit = 10; localparam s_idle_in_6 = 1 << s_idle_in_6_bit ;
+  localparam s_idle_in_5_bit = 11; localparam s_idle_in_5 = 1 << s_idle_in_5_bit ;
+  localparam s_idle_in_4_bit = 12; localparam s_idle_in_4 = 1 << s_idle_in_4_bit ;
+  localparam s_idle_in_3_bit = 13; localparam s_idle_in_3 = 1 << s_idle_in_3_bit ;
+  localparam s_idle_in_2_bit = 14; localparam s_idle_in_2 = 1 << s_idle_in_2_bit ;
+  localparam s_idle_in_1_bit = 15; localparam s_idle_in_1 = 1 << s_idle_in_1_bit ;
+
+  (* onehot *)
+  reg [15:0] state = s_init;
+
+  // ----------------------------------------------------------
+  // -- Access control wires
+  // ----------------------------------------------------------
+
+  reg [14:0] reset_counter = sdram_startup_cycles;
+  reg  [7:0] refresh_counter = 0;
+  reg        refresh_pending = 1;
+  reg           rd_sticky  = 0;
+  reg  [3:0] wmask_sticky  = 4'b0000;
+
+  wire stillatwork = ~(state[s_read_5_bit] | state[s_write_2_bit]);
+  wire [8:0] refresh_counterN = refresh_counter - 1;
+
+  // ----------------------------------------------------------
+  // -- The memory controller
+  // ----------------------------------------------------------
+
+  always @(posedge clk)
+    if(!resetn) begin
+      state         <= s_init;
+      reset_counter <= sdram_startup_cycles; // Counts backwards to zero
+      busy          <= 0;  // Technically, we are busy with initialisation, but there are no ongoing read or write requests
+      rd_sticky     <= 0;
+      wmask_sticky  <= 4'b0000;
+      sd_cke        <= 0;
+    end else begin
+
+      // FemtoRV32 pulses read and write lines high for exactly one clock cycle.
+      // Address and data lines keep stable until busy is released.
+      // Therefore: Take note of the requested read or write, and assert busy flag immediately.
+
+         busy      <= ((|wmask) | rd) | (busy         &    stillatwork   );
+         rd_sticky <=             rd  | (rd_sticky    &    stillatwork   );
+      wmask_sticky <=    wmask        | (wmask_sticky & {4{stillatwork}} );
+
+      // Schedule refreshes regularly
+      refresh_counter <= refresh_counterN[8] ? sdram_refresh_cycles : refresh_counterN[7:0];
+      refresh_pending <= (refresh_pending & ~state[s_idle_bit]) | refresh_counterN[8];
+
+      (* parallel_case *)
+      case(1'b1)
+
+        // Processor can already request the first read or write here, but has to wait then:
+
+        state[s_init_bit]: begin
+
+          //------------------------------------------------------------------------
+          //-- This is the initial startup state, where we wait for at least 100us
+          //-- before starting the start sequence
+          //--
+          //-- The initialisation is sequence is
+          //--  * de-assert SDRAM_CKE
+          //--  * 100us wait,
+          //--  * assert SDRAM_CKE
+          //--  * wait at least one cycle,
+          //--  * PRECHARGE
+          //--  * wait 2 cycles
+          //--  * REFRESH,
+          //--  * tREF wait
+          //--  * REFRESH,
+          //--  * tREF wait
+          //--  * LOAD_MODE_REG
+          //--  * 2 cycles wait
+          //------------------------------------------------------------------------
+
+          sd_ba  <= 2'b00;    // Reserved for future use in mode configuration
+          sd_dqm <= 2'b11;    // Data bus in High-Z state
+          sd_data_drive <= 0; // Do not drive the data bus now
+
+          case (reset_counter) // Counts from a large value down to zero
+
+            33: begin sd_cke <= 1; end
+
+            // Ensure all rows are closed
+            31: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_PRECHARGE; sd_addr <= 13'b0010000000000; end
+
+            // These refreshes need to be at least tRFC (63ns) apart
+            23: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_AUTO_REFRESH; end
+            15: begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_AUTO_REFRESH; end
+
+            // Now load the mode register
+            7:  begin {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_LOAD_MODE; sd_addr <= MODE; end
+
+            default:  {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
+          endcase
+
+          reset_counter <= reset_counter - 1;
+          if (reset_counter == 0) state <= s_idle;
+        end
+
+        // New read or write requests from the processor may arrive in these states:
+
+        //-----------------------------------------------------
+        //-- Additional NOPs to meet timing requirements
+        //-----------------------------------------------------
+
+        state[s_idle_in_6_bit]: begin state <= s_idle_in_5;  {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
+        state[s_idle_in_5_bit]: begin state <= s_idle_in_4;  {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
+        state[s_idle_in_4_bit]: begin state <= s_idle_in_3;  {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
+        state[s_idle_in_3_bit]: begin state <= s_idle_in_2;  {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
+        state[s_idle_in_2_bit]: begin state <= s_idle_in_1;  {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
+        state[s_idle_in_1_bit]: begin state <= s_idle;       {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP; end
+
+        // Refresh cycle needs tRFC (63ns), so 6 idle cycles are needed @ 100MHz
+
+        //-----------------------------------------------------
+        //-- Dispatch all possible actions while idling (NOP)
+        //-----------------------------------------------------
+
+        state[s_idle_bit]: begin
+          sd_ba                          <= addr[23:22];                 // Bank select, 2 bits
+          sd_addr                        <= {addr[25:24], addr[21:11]} ; // RA0-RA12: 8192 Row address
+
+          {sd_cs, sd_ras, sd_cas, sd_we} <= refresh_pending             ? CMD_AUTO_REFRESH :
+                                            (|wmask_sticky) | rd_sticky ? CMD_ACTIVE :
+                                                                          CMD_NOP;
+
+          state                          <= refresh_pending             ? s_idle_in_2 : // *** Experimental result: Direct transition to s_idle does not work @ 40 MHz, s_idle_in_1 is unstable, sd_idle_in_2 is fine.
+                                            (|wmask_sticky) | rd_sticky ? s_activate :
+                                                                          s_idle;
+        end
+
+        // Busy flag is set while state machine is in the following states:
+
+        //-----------------------------------------------------
+        //-- Opening the row ready for reads or writes
+        //-----------------------------------------------------
+
+        state[s_activate_bit]: begin
+          sd_data_drive                  <= ~rd_sticky;  // Drive or release bus early, before the SDRAM chip takes over to drive these lines
+          {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
+          state                          <= rd_sticky ? s_read_1 : s_write_1;
+        end
+
+        // RAS-to-CAS delay, also necessary for precharge, used in this state machine: 2 cycles.
+        // Specification of AS4C32M16SB-7TCN: 21 ns --> Good for 1/(21e-9 / 2) = 95.23 MHz
+
+        //-----------------------------------------------------
+        //-- Processing the read transaction
+        //-----------------------------------------------------
+
+        state[s_read_1_bit]: begin
+          sd_dqm                         <= 2'b00; // SDRAM chip shall drive the bus lines
+          {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_READ;
+          sd_addr                        <= {3'b001, addr[10:2], 1'b0}; // Bit 10: Auto-precharge. CA0-CA9: 1024 Column address.
+          state                          <= s_read_2;
+        end
+
+        state[s_read_2_bit]: begin
+          {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
+          state                          <= s_read_3;
+        end
+
+        state[s_read_3_bit]: state       <= s_read_4;
+
+
+        state[s_read_4_bit]: begin
+          dout[15:0]                     <= sd_data_in;
+          state                          <= s_read_5;
+        end
+
+        // Busy is cleared when reaching this state, fulfilling the request:
+
+        state[s_read_5_bit]: begin
+          dout[31:16]                    <= sd_data_in;
+          state                          <= s_idle;  // *** Experimental result: Direct transition to s_idle is fine @ 40 MHz
+        end
+
+        // Precharge (which is automatic here) needs 21 ns, therefore 2 idle cycles need to be inserted
+
+        //-----------------------------------------------------
+        // -- Processing the write transaction
+        //-----------------------------------------------------
+
+        state[s_write_1_bit]: begin
+          sd_addr                        <= {3'b001, addr[10:2], 1'b0}; // Bit 10: Auto-precharge. CA0-CA9: 1024 Column address.
+          sd_data_out                    <= din[15:0];
+          sd_dqm                         <= ~wmask_sticky[1:0];
+          {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_WRITE;
+          state                          <= s_write_2;
+        end
+
+        // Busy is cleared when reaching this state, fulfilling the request:
+
+        state[s_write_2_bit]: begin
+          sd_data_out                    <= din[31:16];
+          sd_dqm                         <= ~wmask_sticky[3:2];
+          {sd_cs, sd_ras, sd_cas, sd_we} <= CMD_NOP;
+          state                          <= s_idle_in_2; // *** Experimental result: s_idle_in_1 does not work @ 40 MHz, s_idle_in_2 is fine.
+        end
+
+        // Write needs 14 ns internally, then Precharge needs 21 ns, therefore 3 idle cycles need to be inserted
+
+      endcase
+   end
+
+endmodule
blob - /dev/null
blob + b46f46b08fb3efb36817319f1d5974843ac2b8db (mode 644)
--- /dev/null
+++ src/sdram_test.v
@@ -0,0 +1,75 @@
+
+module top(
+	   output	    sdram_clk,
+	   output	    sdram_cke,
+	   output	    sdram_csn,
+	   output	    sdram_wen,
+	   output	    sdram_rasn,
+	   output	    sdram_casn,
+	   output [12:0]    sdram_a,
+	   output [1:0]	    sdram_ba,
+	   output [1:0]	    sdram_dqm,
+	   inout [15:0]	    sdram_d,
+	   
+	   input	    clk_25mhz,
+	   input [6:0]	    btn,
+	   input [3:0]	    sw,
+	   output [7:0] led,
+	   output	    wifi_gpio0
+	   );
+
+   reg [25:0] addr = 0;
+   reg [31:0] counter = 0;
+   wire [31:0] data;
+   wire	      wr, rd, clk_40mhz, rst;
+   
+   Clock1 _clk1(
+		.clkin(clk_25mhz),
+		.clkout0(clk_40mhz),
+		.locked()
+		);
+   SDRAM _sdram(
+		.sd_clk(sdram_clk),
+		.sd_cke(sdram_cke),
+		.sd_d(sdram_d),
+		.sd_addr(sdram_a),
+		.sd_ba(sdram_ba),
+		.sd_dqm(sdram_dqm),
+		.sd_cs(sdram_csn),
+		.sd_we(sdram_wen),
+		.sd_ras(sdram_rasn),
+		.sd_cas(sdram_casn),
+	       
+		.clk(clk_40mhz),
+		.resetn(!rst),
+		.wmask(wr ? 4'b0001 : 4'b0),
+		.rd(rd),
+		.addr(addr),
+		.din(counter),
+		.dout(data),
+		.busy(led[7])
+		);
+
+   wire change_counter = btn[3] || btn[4];
+   wire	change_addr = btn[5] || btn[6];
+
+   wire [31:0] next_counter = btn[3] ? counter + 1 : counter - 1;
+   wire [25:0] next_addr = btn[5] ? addr - 4 : addr + 4;
+   
+   always@ (posedge change_counter or posedge rst)
+      if (rst)
+	counter <= 0;
+      else
+	counter <= next_counter;
+
+   always@ (posedge change_addr or posedge rst)
+     if (rst)
+       addr <= 0;
+     else
+       addr <= next_addr;
+
+   assign rd = btn[1];
+   assign wr = btn[2];
+   assign rst = ~btn[0];
+   assign led[6:0] = data[6:0];
+endmodule
blob - /dev/null
blob + cdbd75268ed63f22f0c4aa2f2e8e3fdab70f02d5 (mode 644)
--- /dev/null
+++ src/top.v
@@ -0,0 +1,141 @@
+module ClockDivider(
+		    input  rst,
+		    input  clk_in,
+		    output clk_out
+	      );
+
+   reg clk = 0;
+   reg [31:0] counter = 0;
+
+   always@ (posedge clk_in or posedge rst)
+     begin
+	if (rst)
+	  begin
+	     counter <= 0;
+	     clk <= 0;
+	  end
+	else if (counter == 32'd1_000_000)
+	  begin
+	     counter <= 0;
+	     clk <= ~clk;
+	  end
+	else
+	  counter <= counter + 1;
+   end
+
+   assign clk_out = ~clk;
+endmodule
+
+module top(
+	   output	    sdram_clk,
+	   output	    sdram_cke,
+	   output	    sdram_csn,
+	   output	    sdram_wen,
+	   output	    sdram_rasn,
+	   output	    sdram_casn,
+	   output [12:0]    sdram_a,
+	   output [1:0]	    sdram_ba,
+	   output [1:0]	    sdram_dqm,
+	   inout [15:0]	    sdram_d,
+	   
+	   input	    clk_25mhz,
+	   input [6:0]	    btn,
+	   input [3:0]	    sw,
+	   output reg [7:0] led,
+	   output	    wifi_gpio0
+);
+
+   wire [15:0] mem_addr, mem_data;
+   wire	       mem_wr, mem_en, mem_busy;
+   wire	       clk_40mhz, clk, rst;
+   wire	       sdram;
+   wire [31:0] sdram_dout;
+   reg [15:0]  data;
+
+   wire [15:0] irom [15:0];
+   reg [15:0]  ram [127:0];
+
+   Clock1 _clk1(
+		  .clkin(clk_25mhz),
+		  .clkout0(clk_40mhz),
+		  .locked()
+		  );
+
+   ClockDivider _clk(
+		     .clk_in(clk_40mhz),
+		     .clk_out(clk),
+		     .rst(rst)
+		     );
+
+   Processor _cpu(
+		  .debug(),
+		  .mem_addr(mem_addr),
+		  .mem_data(mem_data),
+		  .mem_wr(mem_wr),
+		  .mem_en(mem_en),
+		  .mem_busy(mem_busy),
+		  .clk(clk),
+		  .rst(rst)
+		  );
+
+   SDRAM _sdram(
+		.sd_clk(sdram_clk),
+		.sd_cke(sdram_cke),
+		.sd_d(sdram_d),
+		.sd_addr(sdram_a),
+		.sd_ba(sdram_ba),
+		.sd_dqm(sdram_dqm),
+		.sd_cs(sdram_csn),
+		.sd_we(sdram_wen),
+		.sd_ras(sdram_rasn),
+		.sd_cas(sdram_casn),
+	       
+		.clk(clk_40mhz),
+		.resetn(!rst),
+		.wmask(sdram && mem_wr ? 4'b0011 : 4'b0000),
+		.rd(sdram && mem_en),
+		.addr({ 18'b0, (mem_addr[7:0] << 1) }),
+		.din({ 16'b0, mem_data }),
+		.dout(sdram_dout),
+		.busy(mem_busy)
+		);
+   
+
+   always@ (posedge clk)
+      if (mem_wr && !mem_en)
+	case (mem_addr[15:8])
+	  8'h10: ram[mem_addr[7:1]] <= mem_data;
+	  8'h80: led <= mem_data[7:0];
+	endcase
+
+   always@ (*) begin
+      case (mem_addr[15:8])
+	8'h00: data <= irom[mem_addr[4:1]];
+	8'h10: data <= ram[mem_addr[8:1]];
+	default: data <= 16'hFFFF;
+      endcase
+   end
+
+   assign sdram = mem_addr[15:8] == 8'h20;
+
+   assign irom[4'h0] = 16'h3180; // 	lui r1, 0x80
+   assign irom[4'h1] = 16'h3220; // 	lui r2, 0x20
+   assign irom[4'h2] = 16'h4302; // l:	lw r3, [r2]
+   assign irom[4'h3] = 16'h8031; // 	sw r3, [r1, 0]
+   assign irom[4'h4] = 16'h2301; // 	addi r3, 1
+   assign irom[4'h5] = 16'h8032; // 	sw r3, [r2, 0]
+   assign irom[4'h6] = 16'hfffb; // 	j l
+   assign irom[4'h7] = 16'hffff; //
+   assign irom[4'h8] = 16'hffff; //
+   assign irom[4'h9] = 16'hffff; //
+   assign irom[4'hA] = 16'hffff; //
+   assign irom[4'hB] = 16'hffff;
+   assign irom[4'hC] = 16'hffff;
+   assign irom[4'hD] = 16'hffff;
+   assign irom[4'hE] = 16'hffff;
+   assign irom[4'hF] = 16'hffff;
+
+   assign wifi_gpio0 = clk;
+   assign mem_data = mem_en && !mem_wr ? (sdram ? sdram_dout[15:0] : data) : 16'bz;
+   assign rst = btn[2];
+endmodule
blob - /dev/null
blob + ce2d0b6d05024d014fbe8360e8a0987e39bfa7a2 (mode 644)
--- /dev/null
+++ ulx3s_v20.lpf
@@ -0,0 +1,455 @@
+BLOCK RESETPATHS;
+BLOCK ASYNCPATHS;
+## ULX3S v2.0 and v2.1
+
+# The clock "usb" and "gpdi" sheet
+LOCATE COMP "clk_25mhz" SITE "G2";
+IOBUF  PORT "clk_25mhz" PULLMODE=NONE IO_TYPE=LVCMOS33;
+FREQUENCY PORT "clk_25mhz" 25 MHZ;
+
+# JTAG and SPI FLASH voltage 3.3V and options to boot from SPI flash
+# write to FLASH possible any time from JTAG:
+SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 MASTER_SPI_PORT=ENABLE SLAVE_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE;
+# write to FLASH possible from user bitstream, not possible form JTAG:
+# SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 MASTER_SPI_PORT=DISABLE SLAVE_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE;
+
+## USBSERIAL FTDI-FPGA serial port "usb" sheet
+LOCATE COMP "ftdi_rxd" SITE "L4"; # FPGA transmits to ftdi
+LOCATE COMP "ftdi_txd" SITE "M1"; # FPGA receives from ftdi
+LOCATE COMP "ftdi_nrts" SITE "M3"; # FPGA receives
+LOCATE COMP "ftdi_ndtr" SITE "N1"; # FPGA receives
+LOCATE COMP "ftdi_txden" SITE "L3"; # FPGA receives
+IOBUF  PORT "ftdi_rxd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "ftdi_txd" PULLMODE=UP IO_TYPE=LVCMOS33;
+IOBUF  PORT "ftdi_nrts" PULLMODE=UP IO_TYPE=LVCMOS33;
+IOBUF  PORT "ftdi_ndtr" PULLMODE=UP IO_TYPE=LVCMOS33;
+IOBUF  PORT "ftdi_txden" PULLMODE=UP IO_TYPE=LVCMOS33;
+
+## LED indicators "blinkey" and "gpio" sheet
+LOCATE COMP "led[7]" SITE "H3";
+LOCATE COMP "led[6]" SITE "E1";
+LOCATE COMP "led[5]" SITE "E2";
+LOCATE COMP "led[4]" SITE "D1";
+LOCATE COMP "led[3]" SITE "D2";
+LOCATE COMP "led[2]" SITE "C1";
+LOCATE COMP "led[1]" SITE "C2";
+LOCATE COMP "led[0]" SITE "B2";
+IOBUF  PORT "led[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "led[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+
+## Pushbuttons "blinkey", "flash", "power", "gpdi" sheet
+LOCATE COMP "btn[0]" SITE "D6";  # BTN_PWRn (inverted logic)
+LOCATE COMP "btn[1]" SITE "R1";  # FIRE1
+LOCATE COMP "btn[2]" SITE "T1";  # FIRE2
+LOCATE COMP "btn[3]" SITE "R18"; # UP W1->R18
+LOCATE COMP "btn[4]" SITE "V1";  # DOWN
+LOCATE COMP "btn[5]" SITE "U1";  # LEFT
+LOCATE COMP "btn[6]" SITE "H16"; # RIGHT Y2->H16
+IOBUF  PORT "btn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "btn[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "btn[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "btn[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "btn[4]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "btn[5]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "btn[6]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+
+## DIP switch "blinkey", "gpio" sheet
+LOCATE COMP "sw[0]" SITE "E8"; # SW1
+LOCATE COMP "sw[1]" SITE "D8"; # SW2
+LOCATE COMP "sw[2]" SITE "D7"; # SW3
+LOCATE COMP "sw[3]" SITE "E7"; # SW4
+IOBUF  PORT "sw[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sw[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sw[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sw[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+
+## SPI OLED DISPLAY SSD1331 (Color) or SSD1306 (B/W) "blinkey", "usb" sheet
+LOCATE COMP "oled_clk" SITE "P4";
+LOCATE COMP "oled_mosi" SITE "P3";
+LOCATE COMP "oled_dc" SITE "P1";
+LOCATE COMP "oled_resn" SITE "P2";
+LOCATE COMP "oled_csn" SITE "N2";
+IOBUF  PORT "oled_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "oled_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "oled_dc" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "oled_resn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "oled_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## SPI Flash chip "flash" sheet
+LOCATE COMP "flash_csn" SITE "R2";
+LOCATE COMP "flash_mosi" SITE "W2";
+LOCATE COMP "flash_miso" SITE "V2";
+LOCATE COMP "flash_holdn" SITE "W1";
+LOCATE COMP "flash_wpn" SITE "Y2";
+#LOCATE COMP "flash_clk" SITE "U3";
+#LOCATE COMP "flash_csspin" SITE "AJ3";
+#LOCATE COMP "flash_initn" SITE "AG4";
+#LOCATE COMP "flash_done" SITE "AJ4";
+#LOCATE COMP "flash_programn" SITE "AH4";
+#LOCATE COMP "flash_cfg_select[0]" SITE "AM4";
+#LOCATE COMP "flash_cfg_select[1]" SITE "AL4";
+#LOCATE COMP "flash_cfg_select[2]" SITE "AK4";
+IOBUF  PORT "flash_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "flash_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "flash_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "flash_holdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "flash_wpn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_clk" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_csspin" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_initn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_cfg_select[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_cfg_select[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "flash_cfg_select[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;
+
+## SD card "sdcard", "usb" sheet
+LOCATE COMP "sd_clk" SITE "H2"; # sd_clk WiFi_GPIO14
+LOCATE COMP "sd_cmd" SITE "J1"; # sd_cmd_di (MOSI) WiFi GPIO15
+LOCATE COMP "sd_d[0]" SITE "J3"; # sd_dat0_do (MISO) WiFi GPIO2
+LOCATE COMP "sd_d[1]" SITE "H1"; # sd_dat1_irq WiFi GPIO4
+LOCATE COMP "sd_d[2]" SITE "K1"; # sd_dat2 WiFi_GPIO12
+LOCATE COMP "sd_d[3]" SITE "K2"; # sd_dat3_csn WiFi_GPIO13
+LOCATE COMP "sd_wp" SITE "P5"; # not connected
+LOCATE COMP "sd_cdn" SITE "N5"; # not connected
+IOBUF  PORT "sd_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sd_cmd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sd_d[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sd_d[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sd_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; # WiFi GPIO12 pulldown bootstrapping requirement
+IOBUF  PORT "sd_d[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sd_wp" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "sd_cdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## ADC SPI (MAX11123) "analog", "ram" sheet
+LOCATE COMP "adc_csn" SITE "R17";
+LOCATE COMP "adc_mosi" SITE "R16";
+LOCATE COMP "adc_miso" SITE "U16";
+LOCATE COMP "adc_sclk" SITE "P17";
+IOBUF  PORT "adc_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "adc_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "adc_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "adc_sclk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## Audio 4-bit DAC "analog", "gpio" sheet
+# 4-bit mode can drive down to 75 ohm load impedance.
+# Lower impedance leads to IO overload, 
+# FPGA will stop working and need reboot.
+# For standard 17 ohm earphones:
+# use bits 2,3 as input (High-Z) and drive only bits 0,1.
+LOCATE COMP "audio_l[3]" SITE "B3"; # JACK TIP (left audio)
+LOCATE COMP "audio_l[2]" SITE "C3";
+LOCATE COMP "audio_l[1]" SITE "D3";
+LOCATE COMP "audio_l[0]" SITE "E4";
+LOCATE COMP "audio_r[3]" SITE "C5"; # JACK RING1 (right audio)
+LOCATE COMP "audio_r[2]" SITE "D5";
+LOCATE COMP "audio_r[1]" SITE "B5";
+LOCATE COMP "audio_r[0]" SITE "A3";
+LOCATE COMP "audio_v[3]" SITE "E5"; # JACK RING2 (video or digital audio)
+LOCATE COMP "audio_v[2]" SITE "F5";
+LOCATE COMP "audio_v[1]" SITE "F2";
+LOCATE COMP "audio_v[0]" SITE "H5";
+IOBUF  PORT "audio_l[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_l[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_l[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_l[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_r[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_r[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_r[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_r[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_v[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_v[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_v[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "audio_v[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+
+## WiFi ESP-32 "wifi", "usb", "flash" sheet
+# other pins are shared with GP/GN, SD card and JTAG
+LOCATE COMP "wifi_en" SITE "F1"; # enable/reset WiFi
+LOCATE COMP "wifi_rxd" SITE "K3"; # FPGA transmits to WiFi
+LOCATE COMP "wifi_txd" SITE "K4"; # FPGA receives from WiFi
+LOCATE COMP "wifi_gpio0" SITE "L2";
+LOCATE COMP "wifi_gpio5" SITE "N4"; # WIFI LED
+LOCATE COMP "wifi_gpio16" SITE "L1"; # Serial1 RX
+LOCATE COMP "wifi_gpio17" SITE "N3"; # Serial1 TX
+# LOCATE COMP "prog_done" SITE "Y3"; # not GPIO, always active
+IOBUF  PORT "wifi_en" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "wifi_rxd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "wifi_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "wifi_gpio16" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF  PORT "wifi_gpio17" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+# IOBUF  PORT "prog_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## PCB antenna 433 MHz (may be also used for FM) "usb" sheet
+LOCATE COMP "ant_433mhz" SITE "G1";
+IOBUF  PORT "ant_433mhz" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+
+## Second USB port "US2" going directly into FPGA "usb", "ram" sheet
+LOCATE COMP "usb_fpga_dp" SITE "E16"; # single ended or differential input only
+LOCATE COMP "usb_fpga_dn" SITE "F16";
+IOBUF  PORT "usb_fpga_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16;
+IOBUF  PORT "usb_fpga_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16;
+LOCATE COMP "usb_fpga_bd_dp" SITE "D15"; # differential bidirectional
+LOCATE COMP "usb_fpga_bd_dn" SITE "E15";
+IOBUF  PORT "usb_fpga_bd_dp" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF  PORT "usb_fpga_bd_dn" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=4;
+LOCATE COMP "usb_fpga_pu_dp" SITE "B12"; # pull up/down control
+LOCATE COMP "usb_fpga_pu_dn" SITE "C12";
+IOBUF  PORT "usb_fpga_pu_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16;
+IOBUF  PORT "usb_fpga_pu_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16;
+
+## JTAG ESP-32 "usb" sheet
+# connected to FT231X and ESP-32
+# commented out because those are dedicated pins, not directly useable as GPIO
+# but could be used by some vendor-specific JTAG bridging (boundary scan) module
+#LOCATE COMP "jtag_tdi" SITE "R5"; # FTDI_nRI   FPGA receives
+#LOCATE COMP "jtag_tdo" SITE "V4"; # FTDI_nCTS  FPGA transmits
+#LOCATE COMP "jtag_tck" SITE "T5"; # FTDI_nDSR  FPGA receives
+#LOCATE COMP "jtag_tms" SITE "U5"; # FTDI_nDCD  FPGA receives
+#IOBUF  PORT "jtag_tdi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "jtag_tdo" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "jtag_tck" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+#IOBUF  PORT "jtag_tms" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## SDRAM "ram" sheet
+LOCATE COMP "sdram_clk" SITE "F19";
+LOCATE COMP "sdram_cke" SITE "F20";
+LOCATE COMP "sdram_csn" SITE "P20";
+LOCATE COMP "sdram_wen" SITE "T20";
+LOCATE COMP "sdram_rasn" SITE "R20";
+LOCATE COMP "sdram_casn" SITE "T19";
+LOCATE COMP "sdram_a[0]" SITE "M20";
+LOCATE COMP "sdram_a[1]" SITE "M19";
+LOCATE COMP "sdram_a[2]" SITE "L20";
+LOCATE COMP "sdram_a[3]" SITE "L19";
+LOCATE COMP "sdram_a[4]" SITE "K20";
+LOCATE COMP "sdram_a[5]" SITE "K19";
+LOCATE COMP "sdram_a[6]" SITE "K18";
+LOCATE COMP "sdram_a[7]" SITE "J20";
+LOCATE COMP "sdram_a[8]" SITE "J19";
+LOCATE COMP "sdram_a[9]" SITE "H20";
+LOCATE COMP "sdram_a[10]" SITE "N19";
+LOCATE COMP "sdram_a[11]" SITE "G20";
+LOCATE COMP "sdram_a[12]" SITE "G19";
+LOCATE COMP "sdram_ba[0]" SITE "P19";
+LOCATE COMP "sdram_ba[1]" SITE "N20";
+LOCATE COMP "sdram_dqm[0]" SITE "U19";
+LOCATE COMP "sdram_dqm[1]" SITE "E20";
+LOCATE COMP "sdram_d[0]" SITE "J16";
+LOCATE COMP "sdram_d[1]" SITE "L18";
+LOCATE COMP "sdram_d[2]" SITE "M18";
+LOCATE COMP "sdram_d[3]" SITE "N18";
+LOCATE COMP "sdram_d[4]" SITE "P18";
+LOCATE COMP "sdram_d[5]" SITE "T18";
+LOCATE COMP "sdram_d[6]" SITE "T17";
+LOCATE COMP "sdram_d[7]" SITE "U20";
+LOCATE COMP "sdram_d[8]" SITE "E19";
+LOCATE COMP "sdram_d[9]" SITE "D20";
+LOCATE COMP "sdram_d[10]" SITE "D19";
+LOCATE COMP "sdram_d[11]" SITE "C20";
+LOCATE COMP "sdram_d[12]" SITE "E18";
+LOCATE COMP "sdram_d[13]" SITE "F18";
+LOCATE COMP "sdram_d[14]" SITE "J18";
+LOCATE COMP "sdram_d[15]" SITE "J17";
+IOBUF PORT "sdram_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_cke" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_csn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_wen" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_rasn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_casn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_a[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_ba[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_ba[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_dqm[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_dqm[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[13]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[14]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "sdram_d[15]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
+
+# GPDI differential interface (Video) "gpdi" sheet
+LOCATE COMP "gpdi_dp[0]" SITE "A16"; # Blue +
+LOCATE COMP "gpdi_dn[0]" SITE "B16"; # Blue -
+LOCATE COMP "gpdi_dp[1]" SITE "A14"; # Green +
+LOCATE COMP "gpdi_dn[1]" SITE "C14"; # Green -
+LOCATE COMP "gpdi_dp[2]" SITE "A12"; # Red +
+LOCATE COMP "gpdi_dn[2]" SITE "A13"; # Red -
+LOCATE COMP "gpdi_dp[3]" SITE "A17"; # Clock +
+LOCATE COMP "gpdi_dn[3]" SITE "B18"; # Clock -
+LOCATE COMP "gpdi_ethp" SITE "A19"; # Ethernet +
+LOCATE COMP "gpdi_ethn" SITE "B20"; # Ethernet -
+LOCATE COMP "gpdi_cec" SITE "A18";
+LOCATE COMP "gpdi_sda" SITE "B19"; # I2C shared with RTC
+LOCATE COMP "gpdi_scl" SITE "E12"; # I2C shared with RTC C12->E12
+IOBUF PORT "gpdi_dp[0]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dn[0]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dp[1]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dn[1]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dp[2]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dn[2]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dp[3]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_dn[3]" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_ethp" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_ethn" IO_TYPE=LVCMOS33D DRIVE=4;
+IOBUF PORT "gpdi_cec" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "gpdi_sda" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT "gpdi_scl" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+# GPIO (default single-ended) "gpio", "ram", "gpdi" sheet
+# Pins enumerated gp[0-27], gn[0-27].
+# With differential mode enabled on Lattice,
+# gp[] (+) are used, gn[] (-) are ignored from design
+# as they handle inverted signal by default.
+# To enable differential, rename LVCMOS33->LVCMOS33D
+LOCATE COMP "gp[0]"  SITE "B11"; # J1_5+  GP0
+LOCATE COMP "gn[0]"  SITE "C11"; # J1_5-  GN0
+LOCATE COMP "gp[1]"  SITE "A10"; # J1_7+  GP1
+LOCATE COMP "gn[1]"  SITE "A11"; # J1_7-  GN1
+LOCATE COMP "gp[2]"  SITE "A9";  # J1_9+  GP2
+LOCATE COMP "gn[2]"  SITE "B10"; # J1_9-  GN2
+LOCATE COMP "gp[3]"  SITE "B9";  # J1_11+ GP3
+LOCATE COMP "gn[3]"  SITE "C10"; # J1_11- GN3
+LOCATE COMP "gp[4]"  SITE "A7";  # J1_13+ GP4
+LOCATE COMP "gn[4]"  SITE "A8";  # J1_13- GN4
+LOCATE COMP "gp[5]"  SITE "C8";  # J1_15+ GP5
+LOCATE COMP "gn[5]"  SITE "B8";  # J1_15- GN5
+LOCATE COMP "gp[6]"  SITE "C6";  # J1_17+ GP6
+LOCATE COMP "gn[6]"  SITE "C7";  # J1_17- GN6
+IOBUF PORT  "gp[0]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[0]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[1]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[1]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[2]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[2]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[3]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[3]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[4]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[4]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[5]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[5]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[6]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[6]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+LOCATE COMP "gp[7]"  SITE "A6";  # J1_23+ GP7
+LOCATE COMP "gn[7]"  SITE "B6";  # J1_23- GN7
+LOCATE COMP "gp[8]"  SITE "A4";  # J1_25+ GP8
+LOCATE COMP "gn[8]"  SITE "A5";  # J1_25- GN8
+LOCATE COMP "gp[9]"  SITE "A2";  # J1_27+ GP9
+LOCATE COMP "gn[9]"  SITE "B1";  # J1_27- GN9
+LOCATE COMP "gp[10]" SITE "C4";  # J1_29+ GP10  WIFI_GPIO27
+LOCATE COMP "gn[10]" SITE "B4";  # J1_29- GN10
+LOCATE COMP "gp[11]" SITE "F4";  # J1_31+ GP11  WIFI_GPIO25
+LOCATE COMP "gn[11]" SITE "E3";  # J1_31- GN11  WIFI_GPIO26
+LOCATE COMP "gp[12]" SITE "G3";  # J1_33+ GP12  WIFI_GPIO32
+LOCATE COMP "gn[12]" SITE "F3";  # J1_33- GN12  WIFI_GPIO33
+LOCATE COMP "gp[13]" SITE "H4";  # J1_35+ GP13  WIFI_GPIO34
+LOCATE COMP "gn[13]" SITE "G5";  # J1_35- GN13  WIFI_GPIO35
+IOBUF PORT  "gp[7]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[7]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[8]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[8]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[9]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[9]"  PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+LOCATE COMP "gp[14]" SITE "U18"; # J2_5+  GP14
+LOCATE COMP "gn[14]" SITE "U17"; # J2_5-  GN14
+LOCATE COMP "gp[15]" SITE "N17"; # J2_7+  GP15
+LOCATE COMP "gn[15]" SITE "P16"; # J2_7-  GN15
+LOCATE COMP "gp[16]" SITE "N16"; # J2_9+  GP16
+LOCATE COMP "gn[16]" SITE "M17"; # J2_9-  GN16
+LOCATE COMP "gp[17]" SITE "L16"; # J2_11+ GP17
+LOCATE COMP "gn[17]" SITE "L17"; # J2_11- GN17
+LOCATE COMP "gp[18]" SITE "H18"; # J2_13+ GP18
+LOCATE COMP "gn[18]" SITE "H17"; # J2_13- GN18
+LOCATE COMP "gp[19]" SITE "F17"; # J2_15+ GP19
+LOCATE COMP "gn[19]" SITE "G18"; # J2_15- GN19
+LOCATE COMP "gp[20]" SITE "D18"; # J2_17+ GP20
+LOCATE COMP "gn[20]" SITE "E17"; # J2_17- GN20
+IOBUF PORT  "gp[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[20]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[20]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+LOCATE COMP "gp[21]" SITE "C18"; # J2_23+ GP21
+LOCATE COMP "gn[21]" SITE "D17"; # J2_23- GN21
+LOCATE COMP "gp[22]" SITE "B15"; # J2_25+ GP22 D15->B15
+LOCATE COMP "gn[22]" SITE "C15"; # J2_25- GN22 E15->C15
+LOCATE COMP "gp[23]" SITE "B17"; # J2_27+ GP23
+LOCATE COMP "gn[23]" SITE "C17"; # J2_27- GN23
+LOCATE COMP "gp[24]" SITE "C16"; # J2_29+ GP24
+LOCATE COMP "gn[24]" SITE "D16"; # J2_29- GN24
+LOCATE COMP "gp[25]" SITE "D14"; # J2_31+ GP25 B15->D14
+LOCATE COMP "gn[25]" SITE "E14"; # J2_31- GN25 C15->E14
+LOCATE COMP "gp[26]" SITE "B13"; # J2_33+ GP26
+LOCATE COMP "gn[26]" SITE "C13"; # J2_33- GN26
+LOCATE COMP "gp[27]" SITE "D13"; # J2_35+ GP27
+LOCATE COMP "gn[27]" SITE "E13"; # J2_35- GN27
+IOBUF PORT  "gp[21]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[21]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[22]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[22]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[23]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[23]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[24]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[24]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[25]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[25]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gp[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+IOBUF PORT  "gn[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## PROGRAMN (reload bitstream from FLASH, exit from bootloader)
+# PCB v2.0.5 and higher
+LOCATE COMP "user_programn" SITE "M4";
+IOBUF  PORT "user_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
+
+## SHUTDOWN "power", "ram" sheet (connected from PCB v1.7.5)
+# on PCB v1.7 shutdown is not connected to FPGA
+LOCATE COMP "shutdown" SITE "G16"; # FPGA receives
+IOBUF  PORT "shutdown" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4;