1`timescale 1ns / 1ps
2/*
3 * This software is Copyright (c) 2016 Denis Burykin
4 * [denis_burykin yahoo com], [denis-burykin2014 yandex ru]
5 * and it is hereby released to the general public under the following terms:
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted.
8 *
9 */
10
11// ********************************************************************
12//
13// template_list is going to replace word_list.
14//
15// * Designed for usage with word_gen_v2
16//
17// * Accepts both PKT_TYPE_WORD_LIST and PKT_TYPE_TEMPLATE_LIST
18//
19// * If word length is WORD_MAX_LEN, then it isn't followed by '\0';
20//   shorter words are '\0' terminated.
21//
22// * In PKT_TYPE_TEMPLATE_LIST, each word is followed by
23//   RANGE_INFO records: RANGES_MAX records 1 byte each.
24//   If some RANGE_INFO record is zero, then it was the last range_info
25//   for the template key.
26//
27// * Last word is followed by a dummy word with word_list_end asserted
28//
29// ********************************************************************
30
31module template_list #(
32	parameter CHAR_BITS = -1, // valid values: 7 8
33	parameter WORD_MAX_LEN = -1,
34	parameter RANGES_MAX = -1,
35	parameter RANGE_INFO_MSB = 1 + `MSB(WORD_MAX_LEN-1)
36	)(
37	input wr_clk,
38	input [7:0] din,
39	input wr_en,
40	output full,
41	input inpkt_end,
42	input is_template_list,
43
44	input rd_clk,
45	output [WORD_MAX_LEN * CHAR_BITS - 1 :0] dout,
46	output [RANGES_MAX * (RANGE_INFO_MSB+1) - 1 :0] range_info,
47	(* USE_DSP48="true" *)
48	output [15:0] word_id,
49	output word_list_end,
50	input rd_en,
51	output empty,
52
53	output reg err_template = 0, err_word_list_count = 0
54	);
55
56	reg full_r = 0;
57	assign full = full_r;
58
59	reg [WORD_MAX_LEN * CHAR_BITS - 1 :0] dout_r = 0;
60	reg [RANGES_MAX * (RANGE_INFO_MSB+1) - 1 :0] range_info_r = 0;
61	reg [15:0] word_id_r = 0;
62	reg word_list_end_r = 0;
63
64	reg [`MSB(WORD_MAX_LEN-1):0] char_count = 0;
65	reg [`MSB(RANGES_MAX-1):0] range_info_count = 0;
66
67
68	localparam [1:0] STATE_WR_WORD = 0,
69					STATE_WR_RANGE_INFO = 1,
70					STATE_RD = 2,
71					STATE_RD_LIST_END = 3;
72
73	(* FSM_EXTRACT="true" *)
74	reg [1:0] state = STATE_WR_WORD;
75
76	always @(posedge wr_clk) begin
77		case (state)
78		STATE_WR_WORD:	if (wr_en) begin
79			if (din == 0) begin
80				// word ends; word of zero length - OK, permitted
81				if (~is_template_list)
82					full_r <= 1;
83				state <= is_template_list ? STATE_WR_RANGE_INFO : STATE_RD;
84			end
85			else begin
86				// next char is going
87				dout_r[(char_count + 1'b1)*CHAR_BITS-1 -:CHAR_BITS] <= din[CHAR_BITS-1:0];
88
89				if (char_count == WORD_MAX_LEN - 1) begin
90					// word is at max.length - word ends
91					if (~is_template_list)
92						full_r <= 1;
93					state <= is_template_list ? STATE_WR_RANGE_INFO : STATE_RD;
94				end
95				else
96					char_count <= char_count + 1'b1;
97			end
98
99			if (inpkt_end) begin
100				word_list_end_r <= 1;
101				if (is_template_list || (din && char_count != WORD_MAX_LEN - 1) )
102					// inexpected pkt_end
103					err_template <= 1;
104			end
105		end
106
107		STATE_WR_RANGE_INFO: if (wr_en) begin
108			if (din == 0) begin
109				// range_info ends
110				full_r <= 1;
111				state <= STATE_RD;
112			end
113			else begin
114				// next item of range_info going
115				range_info_r[(range_info_count + 1'b1)*(RANGE_INFO_MSB+1)-1 -:RANGE_INFO_MSB+1]
116						<= { din[7], din[RANGE_INFO_MSB-1:0] };
117				//if (WORD_MAX_LEN < 64)
118				if (din[6:RANGE_INFO_MSB] != 0)
119					// unexpected content in range_info
120					err_template <= 1;
121
122				if (range_info_count == RANGES_MAX - 1) begin
123					full_r <= 1;
124					state <= STATE_RD;
125				end
126				else
127					range_info_count <= range_info_count + 1'b1;
128			end
129
130			if (inpkt_end) begin
131				word_list_end_r <= 1;
132				if ( din && range_info_count != RANGES_MAX - 1)
133					// inexpected pkt_end
134					err_template <= 1;
135			end
136		end
137
138		STATE_RD: if (rd_en_internal) begin
139			dout_r <= 0;
140			range_info_r <= 0;
141			char_count <= 0;
142			range_info_count <= 0;
143
144			word_list_end_r <= 0;
145			if (word_list_end_r) begin
146				word_id_r <= 0;
147				state <= STATE_RD_LIST_END;
148			end
149			else begin
150				full_r <= 0;
151				word_id_r <= word_id_r + 1'b1;
152				state <= STATE_WR_WORD;
153			end
154
155			if (&word_id_r)
156				// word_id_r overflows
157				err_word_list_count <= 1;
158		end
159
160		// Write dummy word after the end of the list, with word_list_end set
161		STATE_RD_LIST_END: if (rd_en_internal) begin
162			full_r <= 0;
163			state <= STATE_WR_WORD;
164		end
165
166		endcase
167	end
168
169	assign rd_en_internal = ~output_reg_full
170			& (state == STATE_RD || state == STATE_RD_LIST_END);
171
172	cdc_reg #(
173		.WIDTH(WORD_MAX_LEN*CHAR_BITS + RANGES_MAX*(RANGE_INFO_MSB+1) + 16 + 1)
174	) output_reg (
175		.wr_clk(wr_clk),
176		.din({ dout_r, range_info_r, word_id_r, state == STATE_RD_LIST_END }),
177		.wr_en(rd_en_internal), .full(output_reg_full),
178
179		.rd_clk(rd_clk),
180		.dout({ dout, range_info, word_id, word_list_end }),
181		.rd_en(rd_en), .empty(empty)
182	);
183
184endmodule
185