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