-PROJ = master
-PIN_DEF = master.pcf
DEVICE = hx1k
-all: $(PROJ).rpt $(PROJ).bin
+all: toplevel.bin
-%.blif: %.v
+toplevel.bin: master.rpt master.bin worker.rpt worker.bin
+ tools/icestorm/icemulti/icemulti -o toplevel.bin -v -p0 worker.bin master.bin
+
+master.blif: master.v
tools/yosys/yosys -p 'synth_ice40 -top master -blif $@' $<
-%.asc: $(PIN_DEF) %.blif
+worker.blif: worker.v
+ tools/yosys/yosys -p 'synth_ice40 -top worker -blif $@' $<
+
+%.asc: %.pcf %.blif
tools/arachne-pnr/bin/arachne-pnr -d $(subst hx,,$(subst lp,,$(DEVICE))) -o $@ -p $^ -P tq144
%.bin: %.asc
%.rpt: %.asc
tools/icestorm/icetime/icetime -C tools/icestorm/icebox/chipdb-$(subst hx,,$(subst lp,,$(DEVICE))).txt -d $(DEVICE) -mtr $@ $<
-prog: $(PROJ).bin
+prog: toplevel.bin
+ tools/icestorm/iceprog/iceprog $<
+
+progall: toplevel.bin
+ bash progall.sh
+
+progmaster: master.bin
tools/icestorm/iceprog/iceprog $<
clean:
- rm -f $(PROJ).blif $(PROJ).asc $(PROJ).bin
+ rm -f master.blif master.asc worker.blif worker.asc master.bin worker.bin toplevel.bin
sim:
- tools/yosys/yosys -p 'read_verilog -sv -DSIM master.v; prep -top master -nordff; sim -clock CLKin -vcd test.vcd -n 3000'
+ tools/yosys/yosys -p 'read_verilog -sv -DSIM worker.v; prep -top worker -nordff; sim -clock CLKin -vcd test.vcd -n 3000'
.PHONY: all prog clean sim
--- /dev/null
+#!/usr/bin/perl
+use v5.14;
+use warnings;
+use lib '.';
+
+use asm;
+
+sub flag_to_memory {
+ my ($address, $flag) = @_;
+ alu3 alu_zero, alu_select_f, 0, 0, $address, $flag, flag_zero;
+}
+
+storei 0, 5;
+storei 1, 7;
+
+add 0, 1, 0, 4;
+flag_to_memory 1, 4;
use parent qw/Exporter/;
-our @EXPORT = qw/loada loadb store write_verilog alu_select_a alu_select_b alu_select_f alu_zero flag_zero flag_news alu_xor alu_xnor alu_or alu_of_function aluc_add alus_add aluc_addAF alus_addAF alu2 alu3 add addC loadi storei ledm ledi route/;
-our @EXPORT_OK = qw/loada loadb store write_verilog alu_select_a alu_select_b alu_select_f alu_zero flag_zero flag_news alu_xor alu_xnor alu_or alu_of_function aluc_add alus_add aluc_addAF alus_addAF alu2 alu3 add addC loadi storei ledm ledi route/;
+our @EXPORT = qw/loada loadb store write_verilog alu_select_a alu_select_b alu_select_f alu_zero flag_zero flag_news alu_xor alu_xnor alu_or alu_of_function aluc_add alus_add aluc_addAF alus_addAF alu2 alu3 add addC loadi storei ledm ledi route chip_select/;
+our @EXPORT_OK = qw/loada loadb store write_verilog alu_select_a alu_select_b alu_select_f alu_zero flag_zero flag_news alu_xor alu_xnor alu_or alu_of_function aluc_add alus_add aluc_addAF alus_addAF alu2 alu3 add addC loadi storei ledm ledi route chip_select/;
use File::Slurp::Tiny 'write_file';
my $rom_cnt = 0;
+# by default instructions are for all 4 chips to execute
+our $CS = 0xF;
+
sub send_ {
my ($cmd) = @_;
my %cmd = %$cmd;
+ my $op_cs = $cmd{op} + ($CS << 4);
- my $binary = pack 'vCC', @cmd{qw/I mem_addr op/}; # we ignore CS for now
+ my $binary = pack 'vCC', $cmd{I}, $cmd{mem_addr}, $op_cs;
my $hex = reverse unpack 'h*', $binary;
- say "$rom_cnt: data <= 32'h$hex;";
+ # say "$rom_cnt: data <= 32'h$hex;";
+ say $hex;
$rom_cnt++;
}
+sub chip_select {
+ my ($new_cs, $sub) = @_;
+ local $CS = $new_cs;
+ $sub->();
+}
sub nop {
send_
- { I => 0, mem_addr => 0, op => OP_NOP, CS => 0 }
+ { I => 0, mem_addr => 0, op => OP_NOP }
}
sub loada {
$I |= $bsel << 4;
$I |= $aluc << 5;
send_
- { I => $I, mem_addr => $addr, op => OP_LOADA, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_LOADA }
}
sub loadb {
$I |= $inv << 4;
$I |= $alus << 5;
send_
- { I => $I, mem_addr => $addr, op => OP_LOADB, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_LOADB }
}
#sub read_ {
$I |= $edge_ << 7;
$I |= $cube << 8;
send_
- { I => $I, mem_addr => $addr, op => OP_STORE, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_STORE }
}
sub loadi {
my ($addr, $I) = @_;
send_
- { I => $I, mem_addr => $addr, op => OP_LOADI, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_LOADI }
}
sub route {
- my ($addr, $dest_addr) = @_;
+ my ($addr, $dest_addr, $led) = @_;
+ $led //= 0;
my $I = $dest_addr;
+ $I |= $led << 12;
send_
- { I => $I, mem_addr => $addr, op => OP_ROUTE, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_ROUTE }
}
sub storei {
my ($addr, $I) = @_;
send_
- { I => $I, mem_addr => $addr, op => OP_STOREI, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_STOREI }
}
sub led {
my $I = $offset_leds;
$I |= $mode << 4;
send_
- { I => $I, mem_addr => $addr, op => OP_LED, CS => 0 }
+ { I => $I, mem_addr => $addr, op => OP_LED }
}
sub ledm {
my ($addr, $offset) = @_;
+ $offset //= 0;
led $addr, 1, $offset;
}
sub alu_or { alu_of_function { $a | $b | $_ } }
+sub alu_and { alu_of_function { $a & $b & $_ } }
+
+BEGIN {
+ die "alus_add != alu_xor" unless alus_add == alu_xor;
+ die "bad alu_select_f" unless alu_select_f == alu_of_function { $_ };
+}
+
sub alu2 {
my ($aluc, $alus, $addrA, $addrB, $flagr, $flagw, $cond, $inv) = @_;
loada $addrA, $flagr, 0, $aluc;
store $addrC, $flagw, 0, 0;
}
+sub mov {
+ my ($addrA, $addrC) = @_;
+ alu3 alu_zero, alu_select_a, $addrA, $addrA, $addrC, flag_zero, flag_zero
+}
+
sub add {
my ($addrA, $addrB, $addrC, $flag_carry) = @_;
alu3 aluc_add, alus_add, $addrA, $addrB, $addrC, flag_zero, $flag_carry;
alu3 aluc_add, alus_add, $addrA, $addrB, $addrC, $flag_carry, $flag_carry;
}
+sub xor_ {
+ my ($addrA, $addrB, $addrC) = @_;
+ alu3 alu_zero, alu_xor, $addrA, $addrB, $addrC, flag_zero, flag_zero;
+}
+
+sub and_ {
+ my ($addrA, $addrB, $addrC) = @_;
+ alu3 alu_zero, alu_and, $addrA, $addrB, $addrC, flag_zero, flag_zero;
+}
+
# news_gen face partea de mijloc
# news_[mf][mf] face primul alu3, apeleaza news_gen, apoi face ultimul alu3
sub news_generic {
my %dest = %$dest;
while ($nX || $nY) {
my $direction;
- if ($nX && $nY) {
- $nX--;
- $nY--;
- $direction = 7;
- } elsif ($nX) {
+ if ($nX > 0) {
$nX--;
+ $direction = 2;
+ } elsif ($nX < 0) {
+ $nX++;
$direction = 0;
- } else {
+ } elsif ($nY > 0) {
$nY--;
- $direction = 6;
+ $direction = 1;
+ } elsif ($nY < 0) {
+ $nY++;
+ $direction = 3;
}
if ($nX || $nY) { # not the last go
alu3 alu_select_f, alu_select_a, 0, 0, 0, flag_news($direction), flag_zero
--- /dev/null
+#!/usr/bin/perl
+use v5.14;
+use warnings;
+use lib '.';
+
+use asm;
+
+sub update_leds {
+ my ($x) = @_;
+ route $x + 0, $x + 1, 4;
+ route $x + 1, $x + 2, 3;
+ route $x + 2, $x + 3, 2;
+ route $x + 3, $x + 4, 1;
+}
+
+sub xori {
+ my ($addrA, $imm, $addrC) = @_;
+ storei 42, $imm;
+ xor_ $addrA, 42, $addrC;
+}
+
+sub andi {
+ my ($addrA, $imm, $addrC) = @_;
+ storei 42, $imm;
+ and_ $addrA, 42, $addrC;
+}
+
+# we want everyone to have
+#
+# own quadrant at mem[0]
+# east quadrant at mem[11]
+# south quadrant at mem[12]
+# SE quadrant at mem[13]
+
+my $q_east = 11;
+my $q_south = 12;
+my $q_SE = 13;
+
+chip_select 4, sub { storei 0, 0x247 }; # glider
+chip_select $_, sub { ledi $_ } for 1, 2, 4, 8;
+
+
+update_leds 0;
+
+chip_select 1, sub { route 0, 11 };
+chip_select 2, sub { route 11, 12 };
+chip_select 4, sub { route 12, 13 };
+
+chip_select 2, sub { route 0, 13 };
+chip_select 4, sub { route 13, 12 };
+chip_select 8, sub { route 12, 11 };
+
+chip_select 4, sub { route 0, 11 };
+chip_select 8, sub { route 11, 12 };
+chip_select 1, sub { route 12, 13 };
+
+chip_select 8, sub { route 0, 13 };
+chip_select 1, sub { route 13, 12 };
+chip_select 2, sub { route 12, 11 };
+
+
+sub news_to_mem {
+ my ($dir, $mem) = @_;
+ alu3 alu_zero, alu_select_f, 0, 0, $mem, flag_news($dir), flag_zero
+}
+
+my $and_AB = alu_of_function { $a & $b };
+
+sub compute_neighbour {
+ my ($dest, $dir, $mask, $other) = @_;
+ storei $dest, $mask;
+ alu3 $and_AB, $and_AB, 0, $dest, $dest, flag_zero, flag_zero;
+ news_to_mem $dir, $dest;
+
+ storei $dest + 1, 0xFFFF - $mask;
+ alu3 $and_AB, $and_AB, $other, $dest + 1, $dest + 1, flag_zero, flag_zero;
+ alu3 alu_zero, alus_addAF, $dest, 0, $dest, flag_news($dir), flag_zero
+}
+
+# N neighbour
+compute_neighbour 1, 0, 0x0FFF, $q_south;
+# E neighbour
+compute_neighbour 2, 1, 0xEEEE, $q_east;
+# S neighbour
+compute_neighbour 3, 2, 0xFFF0, $q_south;
+# W neighbour
+compute_neighbour 4, 3, 0x7777, $q_east;
+
+sub compute_diagonal_neighbour {
+ my ($dest, $dir, @masks) = @_;
+ die 'expected exactly 4 @masks' unless @masks == 4;
+ storei $dest, $masks[0];
+ alu3 $and_AB, alu_select_a, 0, $dest, 0, flag_zero, flag_zero;
+ news_to_mem $dir, $dest;
+
+ for my $quad (1 .. 3) {
+ storei $dest + 1, $masks[$quad];
+ alu3 $and_AB, alu_select_a, 10 + $quad, $dest + 1, 10 + $quad, flag_zero, flag_zero;
+ alu3 alu_zero, alus_addAF, $dest, 0, $dest, flag_news($dir), flag_zero;
+ }
+}
+
+#update_leds $q_SE;
+
+# NW neighbour
+compute_diagonal_neighbour 5, 4, 0x0777, 0x0888, 0x7000, 0x8000;
+#update_leds 5;
+# NE neighbour
+compute_diagonal_neighbour 6, 5, 0x0EEE, 0x0111, 0xE000, 0x1000;
+#update_leds 6;
+# SE neighbour
+compute_diagonal_neighbour 7, 6, 0xEEE0, 0x1110, 0x000E, 0x0001;
+#update_leds 7;
+# SW neighbour (DODGY)
+compute_diagonal_neighbour 8, 7, 0x7770, 0x8880, 0x0007, 0x0008;
+#update_leds 8;
+
+sub flag_to_memory {
+ my ($address, $flag) = @_;
+ alu3 alu_zero, alu_select_f, 0, 0, $address, $flag, flag_zero;
+}
+
+# now we add bits mem[1] .. mem[8]
+# result should be mem[1] .. mem[3] (we don't distinguish 8 from 0)
+my $F = 1;
+
+# add 1 and 2, 3 and 4, 5 and 6, 7 and 8
+for my $x (1, 3, 5, 7) {
+ add $x, $x + 1, $x, $F;
+ flag_to_memory $x + 1, $F;
+}
+
+# add 12 and 34, 56 and 78
+for my $x (1, 5) {
+ add $x, $x + 2, $x, $F;
+ addC $x + 1, $x + 3, $x + 1, $F;
+ flag_to_memory $x + 2, $F;
+}
+
+# add 12 and 56
+add 1, 5, 1, $F;
+addC 2, 6, 2, $F;
+# F = F | 3 | 7
+alu3 alu_or, alu_select_a, 3, 7, 3, $F, $F;
+
+# should a new cell be born here? (do we have 3 neighbours?)
+sub alu_birth { alu_of_function {; $a && $b && !$_ } }
+
+# should a living cell survive here? (do we have 2 or 3 neighbours?)
+sub alu_survival { alu_of_function {; $a && !$_ } }
+
+# compute the state of this cell at the next tick
+sub alu_step { alu_of_function {; $_ & ($a | $b) } }
+
+# read from memory 1, and with memory 2 and not F, write into memory 1 (= birth-p)
+alu3 alu_zero, alu_birth, 1, 2, 1, $F, flag_zero;
+# read from memory 2, and with flag not F, write into flag F (= survive-p)
+alu3 alu_survival, alu_select_a, 2, 0, 2, $F, $F;
+# read from memory 0, memory 1, and flag F, write F and (mem0 or mem1) into memory 0
+alu3 alu_zero, alu_step, 0, 1, 0, $F, $F; # also zeroes out flag F
`define DIRECTION_W 3'd6
`define DIRECTION_NW 3'd7
-module chip(input clk, input [2:0] op, input [15:0] I, input io_pin, input CS, output reg [15:0] mem_in, input [15:0] mem_out, output reg mem_write, output reg [3:0] led_out = 0);
+module chip(input clk, input [2:0] op, input [15:0] I, output reg [15:0] mem_in, input [15:0] mem_out, output reg mem_write, output reg [3:0] led_out = 0);
// parity is unimplemented
wire edge_ = I[7];
wire [3:0] cube = I[11:8];
- // OP_ROUTE
- wire [5:0] cycle = I[5:0];
- wire [1:0] check = I[7:6];
- wire [3:0] xor_ = I[11:8];
- wire [2:0] snarf = I[14:12];
- wire odd = I[15];
-
- // OP_RUG
- wire rw = I[0];
- wire ac = I[1];
- wire news = I[2];
- wire [4:0] reg_ = I[8:4];
-
// OP_LED
wire mode = I[4];
wire [1:0] offset = I[1:0];
reg [7:0] alu_sum = 0;
reg [7:0] alu_carry = 0;
reg [15:0] cube_in;
- reg io;
// these are not really regs
reg [15:0] alu_sum_out;
reg [15:0] alu_carry_out;
- reg [2:0] alu_index [15:0];
+ reg [2:0] alu_index [15:0];
reg [15:0] idx;
reg [15:0] flag_or_news;
reg [15:0] news_out;
- news newspaper (.news_in(latest_news), .direction(flags_addr[1:0]), .news_out(news_out));
+ news newspaper (.news_in(latest_news), .direction(flags_addr[2:0]), .news_out(news_out));
assign flag_or_news = flags_addr[3] ? news_out : flags_out;
F <= flag_or_news;
A <= mem_out;
C <= mem_out;
- io <= io_pin;
if (bsel)
B <= cube_in;
end
// We transmit a START, then the address (constant 0xE1), then
// [tx_byte], then [more_bytes] bytes from [mb_in]
parameter TX_IDLE = 0;
-parameter TX_ADDRESS = 1;
-parameter TX_FIRST_BYTE = 2;
-parameter TX_MORE_BYTES = 3;
-parameter TX_STOP = 4;
+parameter TX_START = 1;
+parameter TX_ADDRESS = 2;
+parameter TX_FIRST_BYTE = 3;
+parameter TX_MORE_BYTES = 4;
+parameter TX_STOP = 5;
-reg [10:0] tx_clk_divider = CLOCK_DIVIDE;
+reg [30:0] tx_clk_divider = CLOCK_DIVIDE;
reg data_out = 1'b1;
reg clk_out = 1'b1;
reg [7:0] tx_data;
reg [3:0] step = 0;
+reg [5:0] more_bytes_idx = 0;
wire [7:0] address = {7'h70, 1'b0}; // address 0x70, write
wire [15:0] address_data = {address, tx_data};
assign sda = data_out;
assign scl = clk_out;
assign is_transmitting = tx_state != TX_IDLE;
+assign mb_addr = more_bytes_idx;
always @(posedge clk) begin
- // The clk_divider counter counts down from
- // the CLOCK_DIVIDE constant. Whenever it
- // reaches 0, 1/16 of the bit period has elapsed.
- // Countdown timers for the receiving and transmitting
- // state machines are decremented.
- tx_clk_divider = tx_clk_divider - 1;
- if (!tx_clk_divider) begin
- tx_clk_divider = CLOCK_DIVIDE;
- tx_countdown = tx_countdown - 1;
- end
-
- // Transmit state machine
- case (tx_state)
+ if(tx_clk_divider) begin
+ tx_clk_divider <= tx_clk_divider - 1;
+ end else begin
+ tx_clk_divider <= CLOCK_DIVIDE - 1;
+ end
+
+ case (tx_state)
TX_IDLE: begin
if (transmit) begin
- // If the transmit flag is raised in the idle
- // state, save tx_byte for transmission
tx_data = tx_byte;
- // Send the initial, low pulse of 1 bit period
- // to signal the start, followed by the data
- data_out = 0;
- tx_state = TX_ADDRESS;
tx_bits_remaining = 8;
step = 0;
+ tx_state = TX_START;
+ tx_clk_divider <= CLOCK_DIVIDE - 1;
end
end // case: TX_IDLE
+ TX_START: begin
+ if(tx_clk_divider) begin end
+ else begin
+ data_out = 0;
+ tx_state = TX_ADDRESS;
+ end
+ end
+
TX_ADDRESS: begin
- if(step == 0) begin
+ if(tx_clk_divider) begin end
+ else if(step == 0) begin
clk_out <= 0;
step <= 1;
end else if (tx_bits_remaining == 0) begin
end // case: TX_ADDRESS
TX_FIRST_BYTE: begin
- if(step == 0) begin
+ if(tx_clk_divider) begin end
+ else if(step == 0) begin
clk_out <= 0;
step <= 1;
end else if (tx_bits_remaining == 0) begin
end else begin
step <= 0;
- tx_state <= TX_STOP;
+ if(more_bytes)
+ tx_state <= TX_MORE_BYTES;
+ else
+ tx_state <= TX_STOP;
+ tx_bits_remaining <= 8;
+ more_bytes_idx <= 0;
end
end else if(step == 1) begin
data_out <= tx_data[tx_bits_remaining - 1];
end
end // case: TX_FIRST_BYTE
+ TX_MORE_BYTES: begin
+ if(tx_clk_divider) begin end
+ else if(step == 0) begin
+ clk_out <= 0;
+ step <= 1;
+ end else if (tx_bits_remaining == 0) begin
+ if(step == 1) begin
+ data_out <= 0; // really should be z, not 0
+ step <= 2;
+ end else if(step == 2)begin
+ clk_out <= 1;
+ step <= 3;
+ end else begin
+ step <= 0;
+ tx_bits_remaining <= 8;
+
+ if(more_bytes_idx == more_bytes)
+ tx_state <= TX_STOP;
+ more_bytes_idx <= more_bytes_idx + 1;
+ end
+ end else if(step == 1) begin
+ data_out <= mb_in[tx_bits_remaining - 1];
+ step <= 2;
+ end else if(step == 2) begin
+ clk_out <= 1;
+ step <= 3;
+ end else begin // step == 3
+ tx_bits_remaining = tx_bits_remaining - 1;
+ step <= 0;
+ end
+ end // case: TX_MORE_BYTES
+
TX_STOP: begin
- if(step == 0) begin
+ if(tx_clk_divider) begin end
+ else if(step == 0) begin
clk_out <= 0;
step <= 1;
end else if(step == 1) begin
--- /dev/null
+#!/usr/bin/perl
+use v5.14;
+use warnings;
+use lib '.';
+
+use asm;
+
+chip_select 1, sub {
+ storei 0, 0x3;
+};
+
+chip_select 2, sub {
+ storei 0, 0x2;
+};
+
+chip_select 4, sub {
+ storei 0, 0x4;
+};
+
+chip_select 8, sub {
+ storei 0, 0x8;
+};
+
+ledm 0;
--- /dev/null
+#!/usr/bin/perl
+use v5.14;
+use warnings;
+use lib '.';
+
+use asm;
+
+storei 0, 0xF;
+route 0, 9, 1;
+ledi 1;
+news_mm 0, 1, -1, 0;
+route 1, 9, 1;
+ledi 2;
+news_mm 1, 2, -1, 0;
+route 2, 9, 1;
+ledi 4;
+news_mm 2, 3, -1, 0;
+route 3, 9, 1;
+ledi 8;
# 12 MHz clock
set_io CLKin 21
+set_io uart_tx 79
+set_io -pullup yes uart_rx 88
+
+set_io busy_out 87
+set_io -pullup yes busy_in 78
+
# i2c to led matrix
set_io scl 80 # PIO1_04
set_io sda 81 # PIO1_05
+`include "pll.v"
`include "master_rom.v"
`include "i2c.v"
`include "uart.v"
`ifdef SIM
`define UART_DIVIDE 1
+ `define I2C_DIVIDE 4
`else
- `define UART_DIVIDE 1
- // s/192/3/ for 19200 baud uart
+ `define UART_DIVIDE 2048
+ `define I2C_DIVIDE 256
`endif
-module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, output reg ready_out = 1, input ready_in, output scl, output sda);
-// wire clk;
-// wire clk_tmp;
+module master(input CLKin, output [4:0] led, output uart_tx, input uart_rx, output reg busy_out = 0, input busy_in, output scl, output sda);
+ wire clk;
- //pll pll (.clock_in(CLKin), .clock_out(clk));
+ assign clk = CLKin;
- reg [20:0] counter = 0;
+ // ROM
-`ifdef SIM
- wire clk = CLKin;
-`else
- reg clk = 0;
+ reg [7:0] program_counter = 0;
+ // go here at end of program
+ reg [7:0] main = 1;
+ wire [31:0] rom_output;
- always @ (posedge CLKin) begin
- if(counter == 5000) begin
- counter <= 0;
- clk <= 1 - clk;
- end
- else
- counter <= counter + 1;
+ master_rom master_rom (.clk(clk), .addr(program_counter), .data(rom_output));
+
+ wire [2:0] rom_op = rom_output[26:24];
+ wire [2:0] rom_led = rom_output[14:12];
+ wire [3:0] rom_chip_select = rom_output[31:28];
+
+ // if the 4th board won't execute this instruction,
+ // then we won't receive propagation or news
+ wire dont_wait = !rom_chip_select[3];
+ reg [25:0] dont_send = 23'b11111111111111111111111;
+
+ always @(posedge clk) begin
+ if(busy_in)
+ dont_send <= 23'b11111111111111111111111;
+ else if(dont_send)
+ dont_send <= dont_send - 1;
end
-`endif
- reg [3:0] program_counter = 0;
- wire [31:0] rom_output;
+ // state
- master_rom master_rom (.clk(clk), .addr(program_counter), .data(rom_output));
+`define STATE_SEND 0
+`define STATE_WAIT_PROPAGATE 1
+`define STATE_WAIT_NEWS 2
+`define STATE_PROPAGATE_NEWS 3
+`define STATE_SET_LEDS 4
+`define STATE_WASTE_TIME 5
+
+ reg [5:0] state = `STATE_SEND;
+ reg [5:0] uart_ptr = 0;
+
+ reg [30:0] waste_counter = 0;
+ reg [7:0] saved_news [3:0];
+ reg [7:0] sent_byte [3:0];
+
+ reg [7:0] bytes_sent = 0;
+
+ // i2c
+ reg [15:0] leds [3:0];
+
+ initial begin
+ leds[0] <= 16'hF00F;
+ leds[1] <= 16'h0000;
+ leds[2] <= 16'h0000;
+ leds[3] <= 16'hFFFF;
+ end
+
+ /* even rows are green, odd rows are red:
+ * mb_leds[2 * k] is the kth row of green leds
+ * mb_leds[2 * k + 1] is the kth row of red leds
+ */
+ wire [7:0] mb_leds [15:0];
+
+ // all red leds are off
+ assign mb_leds[1] = program_counter;
+ assign mb_leds[3] = 0;
+ assign mb_leds[5] = state;
+ assign mb_leds[7] = uart_ptr;
+ assign mb_leds[9] = 0;
+ assign mb_leds[11] = 0;
+ assign mb_leds[13] = 0;
+ assign mb_leds[15] = 0;
+
+ // green leds, first half
+ assign mb_leds[0] = {leds[1][3:0], leds[0][3:0]};
+ assign mb_leds[2] = {leds[1][7:4], leds[0][7:4]};
+ assign mb_leds[4] = {leds[1][11:8], leds[0][11:8]};
+ assign mb_leds[6] = {leds[1][15:12], leds[0][15:12]};
+
+ // green leds, second half
+ assign mb_leds[8] = {leds[3][3:0], leds[2][3:0]};
+ assign mb_leds[10] = {leds[3][7:4], leds[2][7:4]};
+ assign mb_leds[12] = {leds[3][11:8], leds[2][11:8]};
+ assign mb_leds[14] = {leds[3][15:12], leds[2][15:12]};
+
+ wire [7:0] mb_in;
+ wire [5:0] mb_addr;
+ assign mb_in = mb_leds[mb_addr];
reg [7:0] i2c_tx_byte;
+ reg [5:0] more_bytes = 0;
reg i2c_transmit = 0;
wire i2c_is_transmitting;
- i2c_write i2c (.clk(clk), .scl(scl), .sda(sda), .tx_byte(i2c_tx_byte), .transmit(i2c_transmit), .is_transmitting(i2c_is_transmitting));
+ i2c_write #(.CLOCK_DIVIDE(`I2C_DIVIDE)) i2c (.clk(clk), .scl(scl), .sda(sda), .tx_byte(i2c_tx_byte), .transmit(i2c_transmit), .is_transmitting(i2c_is_transmitting), .more_bytes(more_bytes), .mb_in(mb_in), .mb_addr(mb_addr));
reg [3:0] i2c_init_step = 0;
i2c_transmit <= 1;
i2c_init_step <= 1;
end else if(i2c_init_step == 1) begin
- i2c_tx_byte <= 8'h87; // display on, blink 0.5Hz
+ i2c_tx_byte <= 8'h81; // display on, blink off
i2c_transmit <= 1;
i2c_init_step <= 2;
end else if(i2c_init_step == 2) begin
i2c_tx_byte <= 8'hEF; // max brightness
i2c_transmit <= 1;
i2c_init_step <= 3;
+ end else if(i2c_init_step == 3) begin
+ i2c_tx_byte <= 0;
+ more_bytes <= 16;
+ i2c_transmit <= 1;
+ i2c_init_step <= 4;
+ end else begin
+ i2c_transmit <= 1;
end
end
end
-`define STATE_SEND 0
-`define STATE_WAIT_PROPAGATE 1
-`define STATE_WAIT_NEWS 2
-`define STATE_PROPAGATE_NEWS 3
-`define STATE_WASTE_TIME 4
-
- reg [5:0] state = `STATE_SEND;
- reg [5:0] uart_ptr = 0;
-
wire received;
wire [7:0] rx_byte;
reg transmit = 0;
wire is_receiving;
wire is_transmitting;
- // 19200 (actually 300) baud uart
uart #(.CLOCK_DIVIDE(`UART_DIVIDE)) uart (.clk(clk), .rx(uart_rx), .tx(uart_tx), .received(received), .transmit(transmit), .tx_byte(tx_byte), .rx_byte(rx_byte), .is_receiving(is_receiving), .is_transmitting(is_transmitting));
- reg [15:0] waste_counter = 0;
+ assign led[4] = state != `STATE_WASTE_TIME;
+// assign led[2:0] = rom_op == 6 ? rom_led : 0;
+// assign led[3] = refresh_leds || i2c_is_transmitting;
+// assign led[0] = (leds[0] == 16'h000f);
+// assign led[1] = (leds[0] == 16'h00f0);
+// assign led[2] = (leds[0] == 16'h0111);
+// assign led[3] = (leds[0] == 16'hf00f);
- reg [7:0] saved_news [3:0];
- assign led[4] = state != `STATE_WASTE_TIME;
- assign led[3:0] = i2c_init_step;
+ //assign led[3:0] = saved_news[0][3:0];
+ //assign led[3:0] = program_counter[3:0];
+ assign led[0] = dont_send;
+ assign led[1] = dont_wait;
+ assign led[2] = is_transmitting;
always @(posedge clk) begin
case(state)
if(transmit) begin
transmit <= 0;
end else if(uart_ptr == 4) begin
- program_counter <= program_counter + 1;
uart_ptr <= 0;
- if(rom_output[26:24] == 6) // `OP_ROUTE
+ if(dont_wait)
+ state <= `STATE_WASTE_TIME;
+ else if(rom_op == 6) // `OP_ROUTE
state <= `STATE_WAIT_NEWS;
else
state <= `STATE_WAIT_PROPAGATE;
- end else if(!is_transmitting && ready_in) begin
+ end else if(!is_transmitting && !dont_send) begin
tx_byte <= rom_output[uart_ptr * 8 +: 8];
+ sent_byte[uart_ptr] <= rom_output[uart_ptr * 8 +: 8];
transmit <= 1;
+ bytes_sent <= bytes_sent + 1;
uart_ptr <= uart_ptr + 1;
end
end
end
`STATE_WASTE_TIME: begin
- if(waste_counter == 100) begin
+ if(waste_counter == 50000) begin
waste_counter <= 0;
+ if(program_counter == 255)
+ program_counter <= main;
+ else
+ program_counter <= program_counter + 1;
state <= `STATE_SEND;
end else
waste_counter <= waste_counter + 1;
end // case: `STATE_WAIT_NEWS
`STATE_PROPAGATE_NEWS: begin
- if(uart_ptr == 4) begin
+ if(transmit) begin
+ transmit <= 0;
+ end else if(uart_ptr == 4) begin
+ if(rom_led) begin
+ leds[rom_led - 1] <= (saved_news[1] << 8) + saved_news[0];
+ end
state <= `STATE_WASTE_TIME;
uart_ptr <= 0;
- end else if(!is_transmitting && ready_in) begin
+ end else if(!is_transmitting && !dont_send) begin
tx_byte <= saved_news[uart_ptr];
+ sent_byte[uart_ptr] <= saved_news[uart_ptr];
transmit <= 1;
+ bytes_sent <= bytes_sent + 1;
uart_ptr <= uart_ptr + 1;
end
- end
+ end // case: `STATE_PROPAGATE_NEWS
endcase
end
// ROM module with single input addr, output port, and clock input.
// Data is clocked out of the ROM on positive clock edges.
-module master_rom (input clk, input [3:0] addr, output reg [31:0] data);
+module master_rom (input clk, input [7:0] addr, output reg [31:0] data);
+ reg [31:0] rom [0:255];
+ initial $readmemh("code.hex", rom);
+
always @ (posedge clk) begin
- case(addr)
-0: data <= 32'h04000004;
-1: data <= 32'h04010002;
-2: data <= 32'h07010010;
-3: data <= 32'h00000000;
-4: data <= 32'h00000000;
-5: data <= 32'h06000001;
-6: data <= 32'h07010010;
-7: data <= 32'h00000000;
-8: data <= 32'h00000000;
-9: data <= 32'h04010003;
-10: data <= 32'h07010010;
-11: data <= 32'h00000000;
-12: data <= 32'h00000000;
-13: data <= 32'h06000001;
-14: data <= 32'h07010010;
-15: data <= 32'h00000000;
- endcase
+ data <= rom[addr];
end
endmodule
-module news(input clk, input [15:0] news_in, input [1:0] direction, output [15:0] news_out);
- always @(posedge clk) begin
+module news(input clk, input [15:0] news_in, input [2:0] direction, output [0:15] news_out);
+ always @* begin
case (direction)
-0: news_out = {news_in[12], news_in[13], news_in[14], news_in[15], news_in[ 0], news_in[ 1], news_in[ 2], news_in[ 3], news_in[ 4], news_in[ 5], news_in[ 6], news_in[ 7], news_in[ 8], news_in[ 9], news_in[10], news_in[11]};
-1: news_out = {news_in[ 1], news_in[ 2], news_in[ 3], news_in[ 0], news_in[ 5], news_in[ 6], news_in[ 7], news_in[ 4], news_in[ 9], news_in[10], news_in[11], news_in[ 8], news_in[13], news_in[14], news_in[15], news_in[12]};
-2: news_out = {news_in[ 4], news_in[ 5], news_in[ 6], news_in[ 7], news_in[ 8], news_in[ 9], news_in[10], news_in[11], news_in[12], news_in[13], news_in[14], news_in[15], news_in[ 0], news_in[ 1], news_in[ 2], news_in[ 3]};
-3: news_out = {news_in[ 3], news_in[ 0], news_in[ 1], news_in[ 2], news_in[ 7], news_in[ 4], news_in[ 5], news_in[ 6], news_in[11], news_in[ 8], news_in[ 9], news_in[10], news_in[15], news_in[12], news_in[13], news_in[14]};
+0: news_out = {news_in[11], news_in[10], news_in[ 9], news_in[ 8], news_in[ 7], news_in[ 6], news_in[ 5], news_in[ 4], news_in[ 3], news_in[ 2], news_in[ 1], news_in[ 0], news_in[15], news_in[14], news_in[13], news_in[12]};
+1: news_out = {news_in[12], news_in[15], news_in[14], news_in[13], news_in[ 8], news_in[11], news_in[10], news_in[ 9], news_in[ 4], news_in[ 7], news_in[ 6], news_in[ 5], news_in[ 0], news_in[ 3], news_in[ 2], news_in[ 1]};
+2: news_out = {news_in[ 3], news_in[ 2], news_in[ 1], news_in[ 0], news_in[15], news_in[14], news_in[13], news_in[12], news_in[11], news_in[10], news_in[ 9], news_in[ 8], news_in[ 7], news_in[ 6], news_in[ 5], news_in[ 4]};
+3: news_out = {news_in[14], news_in[13], news_in[12], news_in[15], news_in[10], news_in[ 9], news_in[ 8], news_in[11], news_in[ 6], news_in[ 5], news_in[ 4], news_in[ 7], news_in[ 2], news_in[ 1], news_in[ 0], news_in[ 3]};
+4: news_out = {news_in[10], news_in[ 9], news_in[ 8], news_in[11], news_in[ 6], news_in[ 5], news_in[ 4], news_in[ 7], news_in[ 2], news_in[ 1], news_in[ 0], news_in[ 3], news_in[14], news_in[13], news_in[12], news_in[15]};
+5: news_out = {news_in[ 8], news_in[11], news_in[10], news_in[ 9], news_in[ 4], news_in[ 7], news_in[ 6], news_in[ 5], news_in[ 0], news_in[ 3], news_in[ 2], news_in[ 1], news_in[12], news_in[15], news_in[14], news_in[13]};
+6: news_out = {news_in[ 0], news_in[ 3], news_in[ 2], news_in[ 1], news_in[12], news_in[15], news_in[14], news_in[13], news_in[ 8], news_in[11], news_in[10], news_in[ 9], news_in[ 4], news_in[ 7], news_in[ 6], news_in[ 5]};
+7: news_out = {news_in[ 2], news_in[ 1], news_in[ 0], news_in[ 3], news_in[14], news_in[13], news_in[12], news_in[15], news_in[10], news_in[ 9], news_in[ 8], news_in[11], news_in[ 6], news_in[ 5], news_in[ 4], news_in[ 7]};
endcase
end
my @diffs = (
[-1, 0],
-# [-1, 1],
[0 , 1],
-# [1 , 1],
[1 , 0],
-# [1 , -1],
[0 , -1],
-# [-1, -1]
+ [-1, -1],
+ [-1, 1],
+ [1 , 1],
+ [1 , -1],
);
my @cpus;
for my $direction (0 .. $#diffs) {
print "$direction: news_out = {";
- printf 'news_in[%2d]', $newstable[0][$direction];
- for my $cpu (1 .. ($side * $side - 1)) {
+ printf 'news_in[%2d]', $newstable[$side * $side - 1][$direction];
+ my @lst = 0 .. ($side * $side - 2);
+ for my $cpu (reverse @lst) {
printf ', news_in[%2d]', $newstable[$cpu][$direction];
}
say '};'
--- /dev/null
+#!/bin/bash
+for i in `seq 1 $(lsusb | grep -c 6010)`
+do tools/icestorm/iceprog/iceprog -d i:0x0403:0x6010:$(($i - 1)) toplevel.bin
+done
--- /dev/null
+#!/usr/bin/perl
+use v5.14;
+use warnings;
+use lib '.';
+
+use asm;
+
+storei 0, 0xF;
+route 0, 1, 1;
+nop;
+nop;
+nop;
+nop;
+nop;
+storei 0, 0xF0;
+route 0, 1, 1;
+nop;
+nop;
+nop;
+nop;
+nop;
+storei 0, 0x111;
+route 0, 1, 1;
+nop;
+nop;
+nop;
+nop;
+nop;
parameter TX_SENDING = 1;
parameter TX_DELAY_RESTART = 2;
-reg [10:0] rx_clk_divider = CLOCK_DIVIDE;
-reg [10:0] tx_clk_divider = CLOCK_DIVIDE;
+reg [21:0] rx_clk_divider = CLOCK_DIVIDE;
+reg [21:0] tx_clk_divider = CLOCK_DIVIDE;
reg [2:0] recv_state = RX_IDLE;
reg [5:0] rx_countdown;
--- /dev/null
+# Red LEDs
+set_io led[0] 99
+set_io led[1] 98
+set_io led[2] 97
+set_io led[3] 96
+
+# Green LED
+set_io led[4] 95
+
+# 12 MHz clock
+set_io CLKin 21
+
+set_io uart_tx 79
+set_io -pullup yes uart_rx 88
+
+set_io busy_out 87
+set_io -pullup yes busy_in 78
+
+set_io -pullup yes is_worker 91
\ No newline at end of file
`ifdef SIM
`define UART_DIVIDE 1
`else
- `define UART_DIVIDE 1
- // s/192/3/ for 19200 baud uart
+ `define UART_DIVIDE 2048
`endif
-module worker (input CLKin, output [4:0] led, output uart_tx, input uart_rx, output reg ready_out = 1, input ready_in);
- wire clk;
- wire clk_tmp;
-
- //pll pll (.clock_in(CLKin), .clock_out(clk));
+module worker (input CLKin, output [4:0] led, output uart_tx, input uart_rx, output reg busy_out = 1, input busy_in, input is_worker);
+ wire clk = CLKin;
- reg [20:0] counter = 0;
+`ifndef SIM
+ // if [is_worker == 0], boot into master image
+ SB_WARMBOOT wb (.BOOT (!is_worker), .S0(1'b1), .S1(1'b0));
+`endif
- reg clk = 0;
+`ifdef SIM
+ wire dont_send = 0;
+`else
+ reg [25:0] dont_send = 23'b11111111111111111111111;
- always @ (posedge CLKin) begin
- if(counter == 5000) begin
- counter <= 0;
- clk <= 1 - clk;
- end
- else
- counter <= counter + 1;
+ always @(posedge clk) begin
+ if(busy_in)
+ dont_send <= 23'b11111111111111111111111;
+ else if(dont_send)
+ dont_send <= dont_send - 1;
end
+`endif
wire [11:0] mem_addr;
wire [15:0] mem_in;
wire [15:0] mem_out;
wire mem_write;
+ reg [31:0] the_rom [0:100];
+ reg [10:0] rom_pc = 0;
+
+ initial begin
+ the_rom[0] <= 32'hfC000005;
+ the_rom[1] <= 32'hfC010007;
+ the_rom[2] <= 32'hf9001d00;
+ the_rom[3] <= 32'hfA0112d0;
+ the_rom[4] <= 32'hfB000004;
+ the_rom[5] <= 32'hf9000004;
+ the_rom[6] <= 32'hfA001550;
+ the_rom[7] <= 32'hfB010000;
+ end
+
RAM #(.ADDRESS_BITS(8)) ram (.clk(clk), .write(mem_write), .addr(mem_addr), .in(mem_in), .out(mem_out));
reg [7:0] from_uart [3:0];
wire [15:0] I = {from_uart[1], from_uart[0]};
assign mem_addr = from_uart[2];
wire [2:0] op_from_uart = from_uart[3][2:0];
- wire CS = from_uart[3][3];
+
+ wire [3:0] chip_select = from_uart[3][7:4];
+ wire is_virtual = from_uart[3][3];
+ wire dont_propagate = is_virtual || ~| chip_select;
+ wire not_for_us = !chip_select[0];
+
+ wire [7:0] to_uart [3:0];
+ wire [3:0] next_chip_select = {0, chip_select[3:1]};
+ assign to_uart[0] = from_uart[0];
+ assign to_uart[1] = from_uart[1];
+ assign to_uart[2] = from_uart[2];
+ assign to_uart[3] = {next_chip_select, from_uart[3][3:0]};
/* to execute a ROUTE instruction, we send our neighbour a STOREI
/* instruction with the correct address and value. This is the
assign route_storei[0] = mem_out[7:0];
assign route_storei[1] = mem_out[15:8];
assign route_storei[2] = from_uart[0];
- assign route_storei[3] = 3'd4; // OP_STOREI
+ assign route_storei[3] = 8'h1C; // chip_select = 1, is_virtual = 1, op = OP_STOREI
reg [2:0] op = 0;
reg [2:0] last_op = 0;
reg [15:0] I;
- reg CS;
reg [3:0] led_out;
- chip chip (.clk(clk), .op(op), .I(I), .io_pin(0), .CS(CS), .mem_in(mem_in), .mem_out(mem_out), .mem_write(mem_write), .led_out(led_out));
+ chip chip (.clk(clk), .op(op), .I(I), .mem_in(mem_in), .mem_out(mem_out), .mem_write(mem_write), .led_out(led_out));
wire received;
wire [7:0] rx_byte;
reg [7:0] tx_byte = 0;
wire is_receiving;
wire is_transmitting;
+ wire recv_error;
- // 19200 (actually 300) baud uart
- uart #(.CLOCK_DIVIDE(`UART_DIVIDE)) uart (.clk(clk), .rx(uart_rx), .tx(uart_tx), .received(received), .transmit(transmit), .tx_byte(tx_byte), .rx_byte(rx_byte), .is_receiving(is_receiving), .is_transmitting(is_transmitting));
+ uart #(.CLOCK_DIVIDE(`UART_DIVIDE)) uart (.clk(clk), .rx(uart_rx), .tx(uart_tx), .received(received), .transmit(transmit), .tx_byte(tx_byte), .rx_byte(rx_byte), .is_receiving(is_receiving), .is_transmitting(is_transmitting), .recv_error(recv_error));
`define STATE_IDLE 0
`define STATE_PROPAGATE 1
-`define STATE_EXECUTE 2
-`define STATE_ROUTE 3
+`define STATE_PREEXEC 2
+`define STATE_EXECUTE 3
+`define STATE_ROUTE 4
- reg [5:0] state = `STATE_IDLE;
+ reg [3:0] state = `STATE_IDLE;
- assign led[3:0] = led_out;
- assign led[4] = 0;
+ assign led[3:0] = uart_ptr;
+ assign led[4] = is_receiving;
always @ (posedge clk) begin
case(state)
if(uart_ptr == 4) begin
last_op <= op_from_uart;
uart_ptr <= 0;
- state <= `STATE_PROPAGATE;
- ready_out <= 0;
+ if(dont_propagate)
+ state <= `STATE_PREEXEC;
+ else
+ state <= `STATE_PROPAGATE;
+ busy_out <= 1;
end
else if (received) begin
from_uart[uart_ptr] <= rx_byte;
uart_ptr <= uart_ptr + 1;
- end else
- ready_out <= 1;
+ end else begin
+`ifdef SIM
+ from_uart[0] <= the_rom[rom_pc][7:0];
+ from_uart[1] <= the_rom[rom_pc][15:8];
+ from_uart[2] <= the_rom[rom_pc][23:16];
+ from_uart[3] <= the_rom[rom_pc][31:24];
+ uart_ptr <= 4;
+ rom_pc <= rom_pc + 1;
+`else
+ busy_out <= 0;
+`endif
+ end
end
`STATE_PROPAGATE: begin
transmit <= 0;
else if(uart_ptr == 4) begin
uart_ptr <= 0;
- the_leds <= last_op;
- if(last_op == `OP_ROUTE) begin
- state <= `STATE_ROUTE;
- end else begin
- op <= last_op;
- state <= `STATE_EXECUTE;
- end
- end else if(!is_transmitting && ready_in) begin
- tx_byte <= from_uart[uart_ptr];
+ state <= `STATE_PREEXEC;
+ end else if(!is_transmitting && !dont_send) begin
+ tx_byte <= to_uart[uart_ptr];
transmit <= 1;
uart_ptr <= uart_ptr + 1;
end
+ end // case: `STATE_PROPAGATE
+
+ `STATE_PREEXEC: begin
+ if(not_for_us) begin
+ state <= `STATE_IDLE;
+ end else if(last_op == `OP_ROUTE) begin
+ state <= `STATE_ROUTE;
+ end else begin
+ op <= last_op;
+ state <= `STATE_EXECUTE;
+ end
end
`STATE_EXECUTE: begin
else if(uart_ptr == 4) begin
uart_ptr <= 0;
state <= `STATE_IDLE;
- end else if(!is_transmitting && ready_in) begin
+ end else if(!is_transmitting && !dont_send) begin
tx_byte <= route_storei[uart_ptr];
transmit <= 1;
uart_ptr <= uart_ptr + 1;
end
endcase
end
-
-// wire ready = (state == 0 && !is_receiving);
endmodule