commit - /dev/null
commit + 8515162d03a7aafa6b9f4d109253d9b38575414a
blob - /dev/null
blob + 5d210ac7e7e5bba96d91669f9159c1fc57d61644 (mode 644)
--- /dev/null
+++ .gitignore
+ulx3s.bit
+ulx3s.json
+ulx3s_out.config
+design.json
blob - /dev/null
blob + 4d394a82bcc29895e1da9f1c69082c9f5c048a9a (mode 644)
--- /dev/null
+++ Makefile
+.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
+# 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
+// 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
+// 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
+`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
+
+// 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
+
+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
+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
+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;