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