Attention

This documentation is a work in progress. Expect to see errors and unfinished things.

tgen Source File

  1`timescale 1ns / 1ns
  2
  3// Timing generator
  4// Interposes in and passes-through a local register write bus
  5// One-cycle delay from input bus to output bus
  6// Controlling bus has priority for access to the controlled bus.
  7// Output "collision" when a function generator write gets lost.
  8// That's a single-cycle output, which needs to be latched and/or
  9// counted by whoever instantiates this module.
 10
 11// The dual-port ram holding the program is part of the local bus
 12// address space, defined by an external address decoder that supplies
 13// the dests_write port signal.  The size of that memory is defined
 14// by the pcw parameter.
 15
 16// After filling the table, toggle bank_next to make it take effect.
 17// In theory, you should then wait for that new bank_next value to
 18// propagate to bank_stat before writing anything else to the table.
 19// That's only of real concern if trig inputs are rare.
 20
 21// Features:
 22// Each set of four addresses means:
 23//    time delay
 24//    address to write
 25//    lower half-word of data
 26//    upper half-word of data
 27// time delay is in units of (clock cycles * 2^tgen_gran), applied _after_ the
 28// register write.
 29// Each operation takes four cycles by itself; the delay cycle count adds to
 30// this pedestal.
 31// An address of zero ends the program and resets the state to pc=0,
 32// which restarts when an external trig is supplied.
 33
 34// runs at 150 MHz in Spartan-6 using 93 slice LUTs and one BRAM.
 35
 36// Larry Doolittle, LBNL, 2014
 37
 38module tgen #(
 39     parameter aw = 17,
 40     parameter tgen_gran = 0   // tick extension
 41) (
 42     input clk,  // timespec 6.66 ns
 43     input trig,
 44     output collision,
 45     // Controlling bus
 46     input [31:0] lb_data,
 47     input lb_write,
 48     input [aw-1:0] lb_addr,
 49     input dests_write,
 50     input [aw-17:0] addr_padding,
 51     (* external *)
 52     input bank_next,  // external
 53     // optional monitoring
 54     output [3:0] status,
 55     // These two are not actually used, but they trigger magic from newad
 56     (* external *)
 57     input [31:0] delay_pc_XXX, // external
 58     (* external *)
 59     output [9:0] delay_pc_XXX_addr, // external
 60     // Controlled bus
 61     output [31:0] lbo_data,
 62     output lbo_write,
 63     output [aw-1:0] lbo_addr
 64);
 65
 66localparam pcw=10;  // Set delay_pc_XXX_addr width above to pcw
 67assign delay_pc_XXX_addr = 0;
 68// Counters
 69reg [pcw-1:0] pc=0;
 70wire [1:0] subcycle = pc[1:0];
 71reg [15+tgen_gran:0] timer=0;
 72wire [15:0] new_timer;  // comes from RAM
 73reg mem_zero=0;  // comes from RAM
 74wire zero_addr = (subcycle==3) & mem_zero;
 75wire trig1 = trig & pc==0;
 76reg trig1d=0;
 77reg did_work=0;
 78wire increment = (pc==0) ? trig1d : (subcycle!=0) | (timer==0);
 79always @(posedge clk) begin
 80     trig1d <= trig1;
 81     pc <= pc + increment;
 82     if (zero_addr) pc <= 0;
 83     timer <= (subcycle==1) ? (new_timer<<tgen_gran) : timer-(subcycle==0);
 84     if (trig1) did_work <= 0;
 85     if (pc[2]) did_work <= 1;
 86end
 87
 88// Atomic flip between banks
 89// OK for bank_next to be in some other clock domain
 90reg bank=0, bank_prev=0, work_prev=0;
 91always @(posedge clk) if (trig1) begin
 92     bank <= bank_next;
 93     bank_prev <= bank;
 94     work_prev <= did_work;
 95end
 96
 97// DPRAM
 98wire [15:0] mem_out;
 99wire [pcw:0] addra = { bank, lb_addr[pcw-1:0]};
100wire [pcw:0] addrb = {~bank, pc};
101dpram #(.dw(16), .aw(pcw+1)) dests(.clka(clk), .clkb(clk),
102     .addra(addra), .dina(lb_data[15:0]), .wena(dests_write),
103     .addrb(addrb), .doutb(mem_out));
104
105// Data path for DPRAM read results
106reg [15:0] mem_out1=0, mem_out2=0;
107always @(posedge clk) begin
108     mem_out1 <= mem_out;
109     mem_out2 <= mem_out1;
110     mem_zero <= mem_out==0;
111end
112assign new_timer=mem_out;
113reg write_cycle=0;
114wire [31:0] our_data = write_cycle ? {mem_out1,mem_out} : 32'd0;
115wire [15:0] our_addr = write_cycle ? mem_out2 : 16'd0;
116always @(posedge clk) write_cycle <= subcycle==3 & ~mem_zero;
117
118// Now merge the two streams
119reg [31:0] lbo_data_r=0;
120reg [aw-1:0] lbo_addr_r=0;
121reg lbo_write_r=0, collision_r=0;
122wire write_thru = lb_write & ~dests_write;
123always @(posedge clk) begin
124     lbo_data_r <= write_thru ? lb_data : our_data;
125     lbo_addr_r <= write_thru ? lb_addr : {addr_padding, our_addr};
126     lbo_write_r <= write_thru | write_cycle;
127     collision_r <= write_thru & write_cycle;
128end
129
130// Output ports
131assign lbo_addr = lbo_addr_r;
132assign lbo_data = lbo_data_r;
133assign lbo_write = lbo_write_r;
134assign collision = collision_r;
135assign status = {work_prev, bank_prev, bank, bank_next};
136
137endmodule