/////////////////////////////////////////////////////////////////////////// // FUCKGOATS CPLD circuit. V.3K (December 2016.) // // This was written for an XC9572XL. It SHOULD work on any gate array of // equal or greater size, but no such assurance is given. // It SHOULD also work quite well in the form of an ASIC, or in TTL, or // using any other reasonably-fast logic element. // // (C) 2016 No Such lAbs. // // You do not have, nor can you ever acquire the right to use, copy or // distribute this software ; Should you use this software for any purpose, // or copy and distribute it to anyone or in any manner, you are breaking // the laws of whatever soi-disant jurisdiction, and you promise to // continue doing so for the indefinite future. In any case, please // always : read and understand any software ; verify any PGP signatures // that you use - for any purpose. /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // Knobs. /////////////////////////////////////////////////////////////////////////// // BITS of analogue input used to make each BYTE of accum output. // You MAY want to change if you were to use another type of analogue RNG, // particularly one having a different spectrum from the 'TW' board. `define FOLD_RSIZE 4 // size of counter register `define FOLD 4'd15 // bit count /////////////////////////////////////////////////////////////////////////// // div128, for UART's 115200b on 14.7456MHz crystal. // We also use it as slow clock for the watchdog. module baudgen(clk, clkout); input clk; output clkout; reg [6:0] counter = 0; wire clkout; always @(posedge clk) begin counter <= counter + 1; end assign clkout = (counter == 7'b1111111); endmodule // A very simple TX-only UART. 'No user serviceable parts inside.' // No async reset, because nothing good can come of aborting mid-byte. module ser_tx(clk, baud_clk, fire, txbyte, tx, busy); input clk, baud_clk, fire; input [7:0] txbyte; output tx, busy; wire busy; // Transmitter state machine reg [3:0] state = 0; wire tx_ready = (state == 0); assign busy = ~tx_ready; wire [7:0] txbyteD = txbyte; always @(posedge clk) case(state) 4'b0000: if(fire) state <= 4'b0001; 4'b0001: if(baud_clk) state <= 4'b0100; 4'b0100: if(baud_clk) state <= 4'b1000; // start 4'b1000: if(baud_clk) state <= 4'b1001; // bit 0 4'b1001: if(baud_clk) state <= 4'b1010; // bit 1 4'b1010: if(baud_clk) state <= 4'b1011; // bit 2 4'b1011: if(baud_clk) state <= 4'b1100; // bit 3 4'b1100: if(baud_clk) state <= 4'b1101; // bit 4 4'b1101: if(baud_clk) state <= 4'b1110; // bit 5 4'b1110: if(baud_clk) state <= 4'b1111; // bit 6 4'b1111: if(baud_clk) state <= 4'b0010; // bit 7 4'b0010: if(baud_clk) state <= 4'b0000; // stop1 4'b0011: if(baud_clk) state <= 4'b0000; // stop2 (off) default: if(baud_clk) state <= 4'b0000; endcase reg muxbit; always @(*) case(state[2:0]) 3'd0: muxbit <= txbyteD[0]; 3'd1: muxbit <= txbyteD[1]; 3'd2: muxbit <= txbyteD[2]; 3'd3: muxbit <= txbyteD[3]; 3'd4: muxbit <= txbyteD[4]; 3'd5: muxbit <= txbyteD[5]; 3'd6: muxbit <= txbyteD[6]; 3'd7: muxbit <= txbyteD[7]; endcase reg tx; always @(posedge clk) tx <= (state<4) | (state[3] & muxbit); endmodule // One way to gather entropy from absolute pulse widths. // This was not feasible in v.10K (it could not rely on synced clk.) module eater(clk, nres, in, valid, out); input clk, nres, in; output valid, out; reg [3:0] q; reg out; reg s; always @(posedge clk or negedge nres) if (~nres) begin s <= 0; end else begin s <= in; end wire pulse = s ^ in; // either rise or fall of input always @(posedge clk or negedge nres) if (~nres) begin q <= 0; out <= 0; end else begin out <= q[3] ^ q[2] ^ q[1] ^ q[0]; q <= pulse ? 0 : q + 1; end assign valid = pulse; endmodule // NOTE that if you are doing a yoke test with two units and one set // of analogue RNGs, you must buffer the latter with a latch clocked // from the common clock, so that they obey the hold time constraint // of the CPLD's input buffer. Without this, operation will ~still~ // be repeatable between the yoked units, but ~with errors~. // Von Neumann's 'fair coin' algorithm. module debias(clk, res, fire, in, valid, out); input clk, res, fire, in; output valid, out; reg p, q; reg [1:0] state; wire valid; always @(posedge clk or negedge res) if (~res) begin state <= 0; p <= 0; q <= 0; end else begin case(state) 2'd0: begin p <= in; state <= fire ? 2'd1 : 2'd0; end 2'd1: begin q <= in; state <= fire ? 2'd2 : 2'd1; end 2'd2: begin state <= 2'd0; end 2'd3: // unused begin state <= 2'd0; end endcase end assign valid = (p ^ q) & (state == 2'd2); assign out = p; endmodule // Byte Accumulator. module accum(clk, nres, fire, in, roll, rdy); parameter fold = 8; // bits per shot parameter count_bits = 4; input clk, in, nres, fire; output rdy; output [7:0] roll; reg [7:0] roll; reg [count_bits-1:0] bit_count; reg rdy; always @(posedge clk or negedge nres) begin if (~nres) // async reset begin bit_count <= 0; roll <= 0; rdy <= 0; end else if (fire) begin roll[bit_count[2:0]] <= roll[bit_count[2:0]] ^ in; bit_count <= bit_count + 1; if (bit_count == fold) rdy <= 1; // raise ready flag end end endmodule // Watchdog. Counter, stops advancing at max until reset. module dog(clk, nres, run, alarm); input clk, nres, run; output alarm; reg [5:0] count; assign alarm = (count == 6'b111111); always @(posedge clk or negedge nres) begin if (~nres) // async reset begin count <= 0; end else if (run & ~alarm) begin count <= count + 1; end end endmodule // Werker. module fg(input wire xtal, // on-board clock, from 14.7456MHz resonator inout wire clk, // clock output (master) or input (slave) input wire IN_A, // 'bottom' analogue RNG input, pulled high input wire IN_B, // 'top' analogue RNG input, pulled high output wire ser_tx, // output of UART, to TTL converter output wire SAD // red lamp ); /////////////////////////////////////////////////////////////////////////// // Serial UART /////////////////////////////////////////////////////////////////////////// wire tx; wire fire; wire busy; wire baud_clk; reg [7:0] txbyte; // baud clock generator baudgen bg(clk, baud_clk); // UART. ser_tx sender(clk, baud_clk, fire, txbyte, tx, busy); /////////////////////////////////////////////////////////////////////////// // Master/Slave toggle, for clock-synchronized slave yoke tests. // Yoke two units by connecting the CLK ('RESET' on v1 pcb) pins together. // The first board powered up will become the master. // Slave's red lamp will stay lit, and his clock is inhibited, uses master /////////////////////////////////////////////////////////////////////////// reg [2:0] boot_state = 0; // If I master, I output ~my~ clock; otherwise - I slave, wait for a clock: wire am_master = (boot_state == 3'd6); assign clk = am_master ? xtal : 1'bz; // this pin is pulled high // Breath of Life always @(posedge xtal) // must use on-board oscillator, for obvious reasons begin case(boot_state) default: begin boot_state <= clk ? boot_state + 1 : 3'd7; end 3'd6: // I am a master, for life! (until powerdown) begin boot_state <= 3'd6; end 3'd7: // I am a slave, for life! (until powerdown) begin boot_state <= 3'd7; end endcase end // we want to hold reset for a short while on bootup reg nreset = 0; always @(posedge clk) // use the active clock, ~when we get one~ begin if ((~IN_A) & (~IN_B) & baud_clk) // stay in reset until both rng work begin // and until first baud clock pulse nreset <= 1'd1; end end // We hold ser_tx low on reset, this way it is easy to see when finished. assign ser_tx = tx & nreset; /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // If this device were to grind to a halt, wouldn't you like to know? /////////////////////////////////////////////////////////////////////////// wire dog_alarm; wire dog_run; wire dog_reset; dog sad_dog(clk, dog_reset & nreset, // watchdog reset (if either dips low) dog_run, // watchdog enable-advance dog_alarm); // watchdog alarm, tied to 'SAD' lamp /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// // RNG /////////////////////////////////////////////////////////////////////////// wire eater_a_valid; wire eater_a_out; eater e_a(clk, nreset, IN_A, eater_a_valid, eater_a_out); wire eater_b_valid; wire eater_b_out; eater e_b(clk, nreset, IN_B, eater_b_valid, eater_b_out); wire valid_a; wire outbit_a; debias dbias_a(clk, nreset, eater_a_valid, eater_a_out, valid_a, outbit_a); wire valid_b; wire outbit_b; debias dbias_b(clk, nreset, eater_b_valid, eater_b_out, valid_b, outbit_b); // per xor lemma: wire valid = valid_a | valid_b; wire outbit = outbit_a ^ outbit_b; wire acc_res; // reset accum (active low) wire acc_rdy; // accum has byte wire [7:0] acc_byte; // output of accumulator accum #(.fold(`FOLD), .count_bits(`FOLD_RSIZE)) acc_a(clk, acc_res & nreset, // if either low valid, outbit, acc_byte, acc_rdy); /////////////////////////////////////////////////////////////////////////// // device cycles forever through four phases reg [1:0] state; always @(posedge clk or negedge nreset) if (~nreset) begin state <= 0; txbyte <= 0; end else begin case(state) 2'd0: // ground state begin txbyte <= 0; // clear tx buffer state <= 2'd1; // next end 2'd1: begin // wait for 'a' accum if (acc_rdy) begin txbyte <= acc_byte; state <= 2'd2; // next end else state <= 2'd1; // wait end 2'd2: begin // clear 'a'; fire tx state <= 2'd3; // next end 2'd3: begin // wait to end of tx state <= (~busy) ? 2'd0 : 2'd3; // wait for UART end endcase end assign fire = (state == 2'd2); // txbyte is ready, so fire the UART assign acc_res = ~(fire); // zap acc_byte (active low) // advance dogometer at the baud clock rate assign dog_run = baud_clk & (state == 2'd1); assign dog_reset = acc_res; // reset dogometer we got a byte // Red 'Sadness' lamp. // If STEADILY LIT, check ALL connections: the device is halted and is // doing NO USEFUL WORK at all! // Re-seat analogue RNG boards, check their power supplies (if you have, // e.g., a custom isolation system.) Otherwise - one or both analogue // RNG units may need replacement. assign SAD = dog_alarm | ~am_master | (~nreset); // Red lamp will also light if the board is placed into the slave // state (for verification) using the external clock // (marked RESET on early board revs) pin; and when in RESET state. endmodule /////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////