Attention

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

circle_buf Source File

  1`timescale 1ns / 1ns
  2
  3module circle_buf #(
  4     parameter dw=16,
  5     parameter aw=13,
  6     parameter stat_w=16, // Width of buffer statistics
  7     // For each half of the double-buffered memory, the default is to use a read of the last
  8     // memory location as acknowledgement that the buffer read is complete,
  9     // and read cycles need the stb_out signal set high to register.
 10     // Set this parameter to 0 to disable that feature, in which case you
 11     // need to construct the stb_out signal as a simple explicit flip command.
 12     parameter auto_flip = 1
 13) (
 14     // source side
 15     input          iclk,
 16     input [dw-1:0] d_in,
 17     input          stb_in,          // d_in is valid
 18     input          boundary,        // between blocks of input strobes
 19     input          stop,            // single-cycle
 20                                     // assume stop happens a programmable number of samples
 21                                     // after a fault event, and we will save 1024 samples
 22                                     // before the stop signal
 23     output         buf_sync,        // single-cycle when buffer starts/ends
 24     output         buf_transferred, // single-cycle when a buffer has been
 25                                     // handed over for reading;
 26                                     // one cycle delayed from buf_sync
 27
 28     // readout side
 29     input                 oclk,
 30     output                enable,
 31     input  [aw-1:0]       read_addr, // nominally 8192 locations
 32     output [dw-1:0]       d_out,
 33     input                 stb_out,
 34     output [stat_w-1:0]   buf_count, // number of full buffer writes
 35     output [aw-1:0]       buf_stat2, // last valid location
 36     output [stat_w-1:0]   buf_stat,  // includes fault bit, and (if set) the last valid location
 37     output [aw+4:0]       debug_stat // {stb_in, boundary, btest, wbank, rbank, wr_addr}
 38);
 39
 40`define MIN(a,b) a < b ? a : b
 41
 42// parameterized to improve testability
 43
 44// 8192 words of 16 bits, double buffered
 45// maybe subdivided into 1024 time samples of 4 RF waveforms,
 46// each with an I and Q component
 47
 48// readout side state
 49reg rbank=0;  // really complement
 50
 51// source side control logic
 52// Flow control is opposite that in decay_buf: the pacing happens
 53// from the readout side
 54wire flag_return;   // buffer request from readout side
 55wire flag_return_x;  // and converted to the iclk domain
 56reg_tech_cdc flag_return_cdc(.I(flag_return), .C(iclk), .O(flag_return_x));
 57reg [aw-1:0] write_addr=0, save_addr=0, save_addr0=0;
 58reg pend=0, run=1, wbank=0;
 59wire change_req = wbank ^ flag_return_x;
 60wire end_write_addr = &write_addr;
 61reg record_type=1;
 62reg boundary_ok=1;
 63reg [1:0] done_read = 2'b0;
 64wire stop_write=pend & boundary;
 65wire eval_done_read = stb_in & boundary_ok & end_write_addr;
 66wire eval_block=eval_done_read & change_req;
 67reg buff_wrap=0;
 68reg buf_transferred_r=0;
 69wire btest= boundary | ( stb_in & end_write_addr );
 70assign buf_transferred = buf_transferred_r;
 71always @(posedge iclk) begin
 72     if (eval_done_read) done_read[wbank] <= change_req;
 73     //if (boundary|(stb_in&end_write_addr)) boundary_ok <= boundary;
 74     if (btest) boundary_ok <= boundary;
 75     if (stb_in & boundary_ok) write_addr <= write_addr+1; //wbank==rbank ? 0 : write_addr+1;
 76     if (eval_block) begin
 77             run <= 1;
 78             wbank <= ~wbank;
 79             record_type <= run;  // fault (0) vs. comfort display (1)
 80             save_addr0 <= run ? {aw{1'b0}} : save_addr;
 81     end
 82     buf_transferred_r <= eval_block;
 83     if ((stop & run)| boundary) pend <= stop & run;  // ignore double stops
 84     if (stop_write) begin
 85             run <= 0;
 86             save_addr <= write_addr;
 87             buff_wrap <= ~done_read[wbank];
 88     end
 89end
 90wire flag_send=wbank;  // says "I want to write bank foo"
 91assign debug_stat={stb_in,boundary,btest,wbank,rbank,write_addr};
 92// Handshake means "OK, I won't read bank foo"
 93
 94// readout side control logic
 95wire flag_send_x;
 96reg_tech_cdc flag_send_cdc(.I(flag_send), .C(oclk), .O(flag_send_x));
 97wire [aw-1:0] read0_addr = read_addr;  // cut down to current width
 98wire end_read_addr = &read0_addr;
 99assign enable = ~flag_send_x ^ rbank;
100wire flip_buffer = stb_out & enable & (end_read_addr | ~auto_flip);
101always @(posedge oclk) begin
102     if (flip_buffer) rbank <= ~rbank;
103end
104assign flag_return = rbank;
105
106// in iclk domain
107wire [stat_w-2-1:0] save_addr0_ext = save_addr0[`MIN(stat_w-2,aw)-1:0]; // truncate or pad MSBs
108assign buf_stat = {record_type, buff_wrap, save_addr0_ext};
109
110assign buf_stat2 = save_addr0;
111wire write_en= stb_in & boundary_ok & run;
112
113// Count of how many buffers we have acquired
114reg [stat_w-1:0] buf_count_r=0;
115always @(posedge iclk) if (eval_done_read) buf_count_r <= buf_count_r+1;
116assign buf_count=buf_count_r;
117assign buf_sync=eval_done_read;
118
119// data path is simply a dual-port RAM
120dpram #(.aw(aw+1), .dw(dw)) cbuf(.clka(iclk), .clkb(oclk),
121     .addra({wbank,write_addr}), .dina(d_in), .wena(write_en),
122     .addrb({~rbank,read0_addr}), .doutb(d_out)
123);
124
125endmodule