基于FPGA的ASCII码日期转时间戳算法实现
本篇为学员项目经验分享。
画师,执笔绘画FPGA江湖 持续更新 欢迎关注!
- 基于FPGA的ASCII码日期转时间戳算法实现
- 作者:画师
- 地点:上海
- 时间:2020.12.14
一、概念
时间戳是使用数字签名技术产生的数据,签名的对象包括了原始文件信息、签名参数、签名时间等信息。时间戳系统用来产生和管理时间戳,对签名对象进行数字签名产生时间戳,以证明原始文件在签名时间之前已经存在。
在一些通信当中,我们可能会使用ASCII码来进行一些字符串的传输,其中就包括日期等时间数据的传输,而我们的FPGA接收到的就是ASCII码相对应的二进制,不经过转换得到的值就不是原来的值了。而转换成我们想要的时间戳,也需要通过相对应的算法来进行转换,如果得到的值不是原来的值,那么得到的时间戳也将会是错误的,传输到另一端就会解析出错误的值,导致整个传输失败。
二、设计原理
查看ASCII码表可知,规定由8位的二进制数来表示相对应的字符串。而对应的0-9的字符所对应的二进制码分别是0011_0000、0011_0001、0011_0010、0011_0011、0011_0100、0011_0101、0011_0110、0011_0111、0011_1000、0011_1001。通过观察可知,当我们去掉相应的高四位,只留下低四位时,低四位二进制的值正好就是对应的十进制的值。这样我们就能通过相应的算法来得到我们想要的值。
我们可以随意设置一个ASCII码所表示的日期如20201212-20:20:52.。这样,我们首先收到的第一个字符是ASCII码所表示的“2”,也就是二进制所表示的0011_0010,我们去掉高四位留下低四位,然后将第四位的值乘1000,然后将后传输过来的值一次乘100、乘10、乘1,然后再将得到的几个数进行相加,就得到了我们想要的十进制所表示的年2020,后面的值以此类推,去掉无关的字符,只保留对应的数值字符,就可以得到相应的十进制所表示的值,这样就和ASCII码所表示的字符的时间就对上了。
然后,我们就可以使用相对应的Unix时间戳的算法来计算出对应日期的时间戳。Unix时间戳是指从1970年01月01日00时00分00秒到现在的秒数。Unix时间戳的算法可以参照下列式子:
Y=(year-1)*365 year/4-year/100 year/400;
M=367*mon/12-30;
D=day-1;
X=Y M D-719162;
T=((X*24 hour)*60) mim)*60 sec;
第一个中,year/4-year/100 year/400表示的是从0001年到当年的闰年的个数,闰年的天数是366天,所以从 0001年到当年的天数为Y=(year-1)365 year/4-year/100 year/400。在Unix时间戳中,计算月份并不是从1月算起,而是从3月份算起,把3月份当成第一个月会好算一点,用一次函数计算可以得到M=367mon/12-30。然后是日期,是从1号开始的,所以得到的式子就是D=day-1。
由于Unix时间戳是从1970年01月01日00时00分00秒开始算起,所以计算现在的时间戳时,需要减去0001年到1970年01月01日00时00分00秒的时间,因为是从3月份算起,所以0001年已经经过了1月和2月,也就是59天,计算得出的结果为719162,这也就得到了第四个式子X=Y M D-719162。最后就可以得出从1970年01月01日00时00分00秒到当前的秒数,也就是最后一个式子。
由以上原理,我们就可以将ASCII码所表示的时间转换为时间戳了。
三、架构设计
将本设计命名为ascii_2_timestamp,clk为输入的时钟,rst_n为复位信号,ascii表示8位的二进制ASCII码,ivalid为输入的数据有效信号,中间输出值均为转换过后的时间值,再加上一个伴随的数据有效信号,最后输出为time_stamp表示时间戳,done信号告诉下一层转换完成。
四、Verilog代码实现
由于代码较多,这里只贴出部分代码,全部代码下载可以点击左下角阅读原文跳转至叁芯智能科技技术论坛下载,或者登录网址:www.sxznfpga.com。
顶层模块 ascii_2_timestamp 代码:
代码语言:javascript复制1 module ascii_2_timestamp(
2
3 input wire clk,
4 input wire rst_n,
5
6 input wire ivalid,
7 input wire [7:0] ascii,
8
9 output wire [63:0] time_stamp,
10 output wire done
11 );
12
13 wire [15:0] year;
14 wire [7:0] mon;
15 wire [7:0] day;
16 wire [7:0] hour;
17 wire [7:0] min;
18 wire [7:0] sec;
19 wire ovalid;
20
21 ascii_2_bin ascii_2_bin_inst(
22
23 .clk (clk),
24 .rst_n (rst_n),
25
26 .ivalid (ivalid),
27 .ascii (ascii),
28
29 .year (year),
30 .mon (mon),
31 .day (day),
32 .hour (hour),
33 .min (min),
34 .sec (sec),
35
36 .ovalid (ovalid)
37 );
38
39 timestamp timestamp_inst(
40
41 .clk (clk),
42 .rst_n (rst_n),
43
44 .year (year),
45 .mon (mon),
46 .day (day),
47 .hour (hour),
48 .min (min),
49 .sec (sec),
50 .ivalid (ovalid),
51
52 .time_stamp (time_stamp),
53 .done (done)
54 );
55
56 endmodule
ASCII码转换模块 ascii_2_bin 代码:
代码语言:javascript复制1 module ascii_2_bin(
2
3 input wire clk,
4 input wire rst_n,
5
6 input wire ivalid,
7 input wire [7:0] ascii,
8
9 output reg [15:0] year,
10 output reg [7:0] mon,
11 output reg [7:0] day,
12 output reg [7:0] hour,
13 output reg [7:0] min,
14 output reg [7:0] sec,
15
16 output reg ovalid
17 );
18
19 reg [3:0] day_r0;
20 reg [3:0] day_r1;
21 reg [3:0] mon_r0;
22 reg [3:0] mon_r1;
23 reg [3:0] year_r0;
24 reg [3:0] year_r1;
25 reg [3:0] year_r2;
26 reg [3:0] year_r3;
27 reg [3:0] sec_r0;
28 reg [3:0] sec_r1;
29 reg [3:0] min_r0;
30 reg [3:0] min_r1;
31 reg [3:0] hour_r0;
32 reg [3:0] hour_r1;
33
34 always @ (posedge clk,negedge rst_n) begin
35 if (rst_n == 1'b0) begin
36 year_r0 <= 4'd0;
37 year_r1 <= 4'd0;
38 year_r2 <= 4'd0;
39 year_r3 <= 4'd0;
40 mon_r0 <= 4'd0;
41 mon_r1 <= 4'd0;
42 day_r0 <= 4'd0;
43 day_r1 <= 4'd0;
44 end
45 else
46 if (ivalid == 1'b1 && ascii == "-") begin
47 year_r0 <= year_r0;
48 year_r1 <= year_r1;
49 year_r2 <= year_r2;
50 year_r3 <= year_r3;
51 mon_r0 <= mon_r0;
52 mon_r1 <= mon_r1;
53 day_r0 <= day_r0;
54 day_r1 <= day_r1;
55 end
56 else begin
57 day_r0 <= ascii[3:0];
58 day_r1 <= day_r0;
59 mon_r0 <= day_r1;
60 mon_r1 <= mon_r0;
61 year_r0 <= mon_r1;
62 year_r1 <= year_r0;
63 year_r2 <= year_r1;
64 year_r3 <= year_r2;
65 end
66 end
67
68 always @ (posedge clk,negedge rst_n) begin
69 if (rst_n == 1'b0)
70 year <= 16'd0;
71 else
72 if (ivalid == 1'b1 && ascii == "-")
73 year <= year_r3 * 1000 year_r2 * 100 year_r1 * 10 year_r0;
74 else
75 year <= year;
76 end
77
78 always @ (posedge clk,negedge rst_n) begin
79 if (rst_n == 1'b0)
80 mon <= 8'd0;
81 else
82 if (ivalid == 1'b1 && ascii == "-")
83 mon <= mon_r1 * 10 mon_r0;
84 else
85 mon <= mon;
86 end
87
88 always @ (posedge clk,negedge rst_n) begin
89 if (rst_n == 1'b0)
90 day <= 8'd0;
91 else
92 if (ivalid == 1'b1 && ascii == "-")
93 day <= day_r1 * 10 day_r0;
94 else
95 day <= day;
96 end
97
98 always @ (posedge clk,negedge rst_n) begin
99 if (rst_n == 1'b0) begin
100 hour_r0 <= 4'd0;
101 hour_r1 <= 4'd0;
102 end
103 else
104 if (ivalid == 1'b1 && ascii == ":") begin
105 hour_r0 <= hour_r0;
106 hour_r1 <= hour_r1;
107 end
108 else begin
109 hour_r0 <= ascii[3:0];
110 hour_r1 <= hour_r0;
111 end
112 end
113
114 always @ (posedge clk,negedge rst_n) begin
115 if (rst_n == 1'b0)
116 hour <= 4'd0;
117 else
118 if (ivalid == 1'b1 && ascii == ":")
119 hour <= hour_r1 * 10 hour_r0;
120 else
121 hour <= hour;
122 end
123
124 always @ (posedge clk,negedge rst_n) begin
125 if (rst_n == 1'b0) begin
126 min_r0 <= 4'd0;
127 min_r1 <= 4'd0;
128 end
129 else
130 if (ivalid == 1'b1 && ascii == ":") begin
131 min_r0 <= min_r0;
132 min_r1 <= min_r1;
133 end
134 else begin
135 min_r0 <= ascii[3:0];
136 min_r1 <= min_r0;
137 end
138 end
139
140 always @ (posedge clk,negedge rst_n) begin
141 if (rst_n == 1'b0)
142 min <= 4'd0;
143 else
144 if (ivalid == 1'b1 && ascii == ":")
145 min <= min_r1 * 10 min_r0;
146 else
147 min <= min;
148 end
149
150 always @ (posedge clk,negedge rst_n) begin
151 if (rst_n == 1'b0) begin
152 sec_r0 <= 4'd0;
153 sec_r1 <= 4'd0;
154 end
155 else
156 if (ivalid == 1'b1 && ascii == ".") begin
157 sec_r0 <= sec_r0;
158 sec_r1 <= sec_r1;
159 end
160 else begin
161 sec_r0 <= ascii[3:0];
162 sec_r1 <= sec_r0;
163 end
164 end
165
166 always @ (posedge clk,negedge rst_n) begin
167 if (rst_n == 1'b0)
168 sec <= 4'd0;
169 else
170 if (ivalid == 1'b1 && ascii == ".")
171 sec <= sec_r1 * 10 sec_r0;
172 else
173 sec <= sec;
174 end
175
176 always @ (posedge clk,negedge rst_n) begin
177 if (rst_n == 1'b0)
178 ovalid <= 1'b0;
179 else
180 if (ivalid == 1'b1 && ascii == ".")
181 ovalid <= 1'b1;
182 else
183 ovalid <= 1'b0;
184 end
185
186 endmodule
时间戳转换模块 timestamp 代码:
代码语言:javascript复制1 module timestamp(
2
3 input wire clk,
4 input wire rst_n,
5
6 input wire [15:0] year,
7 input wire [7:0] mon,
8 input wire [7:0] day,
9 input wire [7:0] hour,
10 input wire [7:0] min,
11 input wire [7:0] sec,
12 input wire ivalid,
13
14 output reg [63:0] time_stamp,
15 output reg done
16 );
17
18 reg y_req1;
19 reg y_req2;
20 reg x_sum_req;
21 reg [63:0] y;
22 reg [63:0] y_r1;
23 reg [63:0] y_r2;
24
25 reg m_req;
26 reg [63:0] m;
27 reg [63:0] m_r;
28
29 reg [63:0] d;
30
31 reg x_req;
32 reg [63:0] x;
33 reg [63:0] x_r;
34
35 reg t_req1;
36 reg t_req2;
37 reg t_req3;
38 reg [63:0] t_r1;
39 reg [63:0] t_r2;
40
41 always @ (posedge clk,negedge rst_n) begin
42 if (rst_n == 1'b0) begin
43 y_r1 <= 64'd0;
44 y_req1 <= 1'b0;
45 end
46 else
47 if (ivalid == 1'b1) begin
48 y_r1 <= (year - 1) * 365;
49 y_req1 <= 1'b1;
50 end
51 else begin
52 y_r1 <= 1'b0;
53 y_req1 <= 1'b0;
54 end
55 end
56
57 always @ (posedge clk,negedge rst_n) begin
58 if (rst_n == 1'b0) begin
59 y_r2 <= 64'd0;
60 y_req2 <= 1'b0;
61 end
62 else
63 if (y_req1 == 1'b1) begin
64 y_r2 <= y_r1 year/4 - year/100;
65 y_req2 <= 1'b1;
66 end
67 else begin
68 y_r2 <= y_r2;
69 y_req2 <= 1'b0;
70 end
71 end
72
73 always @ (posedge clk,negedge rst_n) begin
74 if (rst_n == 1'b0) begin
75 y <= 64'd0;
76 x_sum_req <= 1'b0;
77 end
78 else
79 if (y_req2 == 1'b1) begin
80 y <= y_r2 year/400;
81 x_sum_req <= 1'b1;
82 end
83 else begin
84 y <= y;
85 x_sum_req <= 1'b0;
86 end
87 end
88
89 always @ (posedge clk,negedge rst_n) begin
90 if (rst_n == 1'b0) begin
91 m_r <= 64'd0;
92 m_req <= 1'b0;
93 end
94 else
95 if (ivalid == 1'b1) begin
96 m_r <= 367 * mon;
97 m_req <= 1'b1;
98 end
99 else begin
100 m_r <= m_r;
101 m_req <= 1'b0;
102 end
103 end
104
105 always @ (posedge clk,negedge rst_n) begin
106 if (rst_n == 1'b0)
107 m <= 64'd0;
108 else
109 if (m_req == 1'b1)
110 m <= m_r/12 29;
111 else
112 m <= m;
113 end
114
115 always @ (posedge clk,negedge rst_n) begin
116 if (rst_n == 1'b0)
117 d <= 64'd0;
118 else
119 if (ivalid == 1'b1)
120 d <= day - 1'b1;
121 else
122 d <= d;
123 end
124
125 always @ (posedge clk,negedge rst_n) begin
126 if (rst_n == 1'b0) begin
127 x_r <= 64'd0;
128 x_req <= 1'b0;
129 end
130 else
131 if (x_sum_req == 1'b1) begin
132 x_r <= y m d;
133 x_req <= 1'b1;
134 end
135 else begin
136 x_r <= x_r;
137 x_req <= 1'b0;
138 end
139 end
140
141 always @ (posedge clk,negedge rst_n) begin
142 if (rst_n == 1'b0) begin
143 x <= 64'd0;
144 t_req1 <= 1'b0;
145 end
146 else
147 if (x_req == 1'b1) begin
148 x <= x_r - 719162;
149 t_req1 <= 1'b1;
150 end
151 else begin
152 x <= x;
153 t_req1 <= 1'b0;
154 end
155 end
156
157 always @ (posedge clk,negedge rst_n) begin
158 if (rst_n == 1'b0) begin
159 t_r1 <= 64'd0;
160 t_req2 <= 1'b0;
161 end
162 else
163 if (t_req1 == 1'b1) begin
164 t_r1 <= x * 24 hour;
165 t_req2 <= 1'b1;
166 end
167 else begin
168 t_r1 <= t_r1;
169 t_req2 <= 1'b0;
170 end
171 end
172
173 always @ (posedge clk,negedge rst_n) begin
174 if (rst_n == 1'b0) begin
175 t_r2 <= 64'd0;
176 t_req3 <= 1'b0;
177 end
178 else
179 if (t_req2 == 1'b1) begin
180 t_r2 <= t_r1 * 60 min;
181 t_req3 <= 1'b1;
182 end
183 else begin
184 t_r2 <= t_r2;
185 t_req3 <= 1'b0;
186 end
187 end
188
189 always @ (posedge clk,negedge rst_n) begin
190 if (rst_n == 1'b0)
191 time_stamp <= 64'd0;
192 else
193 if (t_req3 == 1'b1)
194 time_stamp <= t_r2 * 60 sec;
195 else
196 time_stamp <= 64'd0;
197 end
198
199 always @ (posedge clk,negedge rst_n) begin
200 if (rst_n == 1'b0)
201 done <= 1'b0;
202 else
203 if (t_req3 == 1'b1)
204 done <= 1'b1;
205 else
206 done <= 1'b0;
207 end
208
209 endmodule
五、仿真测试
仿真测试模块 ascii_2_timestamp_tb 代码:
代码语言:javascript复制1 `timescale 1ns/1ps
2
3 module ascii_2_timestamp_tb;
4
5 reg clk;
6 reg rst_n;
7 reg ivalid;
8 reg [7:0] ascii;
9
10 wire [63:0] time_stamp;
11 wire done;
12
13 ascii_2_timestamp ascii_2_timestamp_inst(
14
15 .clk (clk),
16 .rst_n (rst_n),
17
18 .ivalid (ivalid),
19 .ascii (ascii),
20
21 .time_stamp (time_stamp),
22 .done (done)
23 );
24
25 initial clk = 1'b0;
26 always # 10 clk = ~clk;
27
28 initial begin
29 rst_n = 1'b0;
30 ivalid = 1'b0;
31 ascii = 8'd0;
32 # 201;
33
34 rst_n = 1'b1;
35 # 200;
36
37 @ (posedge clk);
38 # 2;
39 ivalid = 1'b1;
40 ascii = "2";
41
42 @ (posedge clk);
43 # 2;
44 ivalid = 1'b1;
45 ascii = "0";
46
47 @ (posedge clk);
48 # 2;
49 ivalid = 1'b1;
50 ascii = "2";
51
52 @ (posedge clk);
53 # 2;
54 ivalid = 1'b1;
55 ascii = "0";
56
57 @ (posedge clk);
58 # 2;
59 ivalid = 1'b1;
60 ascii = "1";
61
62 @ (posedge clk);
63 # 2;
64 ivalid = 1'b1;
65 ascii = "2";
66
67 @ (posedge clk);
68 # 2;
69 ivalid = 1'b1;
70 ascii = "1";
71
72 @ (posedge clk);
73 # 2;
74 ivalid = 1'b1;
75 ascii = "2";
76
77 @ (posedge clk);
78 # 2;
79 ivalid = 1'b1;
80 ascii = "-";
81
82 @ (posedge clk);
83 # 2;
84 ivalid = 1'b1;
85 ascii = "2";
86
87 @ (posedge clk);
88 # 2;
89 ivalid = 1'b1;
90 ascii = "0";
91
92 @ (posedge clk);
93 # 2;
94 ivalid = 1'b1;
95 ascii = ":";
96
97 @ (posedge clk);
98 # 2;
99 ivalid = 1'b1;
100 ascii = "2";
101
102 @ (posedge clk);
103 # 2;
104 ivalid = 1'b1;
105 ascii = "0";
106
107 @ (posedge clk);
108 # 2;
109 ivalid = 1'b1;
110 ascii = ":";
111
112 @ (posedge clk);
113 # 2;
114 ivalid = 1'b1;
115 ascii = "5";
116
117 @ (posedge clk);
118 # 2;
119 ivalid = 1'b1;
120 ascii = "2";
121
122 @ (posedge clk);
123 # 2;
124 ivalid = 1'b1;
125 ascii = ".";
126
127 @ (posedge clk);
128 # 2;
129 ivalid = 1'b0;
130 ascii = 8'd0;
131
132 # 2000;
133 $stop;
134 end
135
136 endmodule
仿真测试结果:
使用20201212-20:20:52最后生成的时间戳为1613161252。
六、总结
在进行一些特殊的转换时,我们可以去研究他的一些规律,实际上将ASCII码转换成十进制也非常简单,只需要把相对应的ASCII码的二进制数转换成十进制即可,但是那样做的话,后面还需要做其他的转换,相对来说就比较麻烦,所以我就选择了这种去掉高四位的方法。
本篇到此结束,后续有时间还会更新,希望各位多多关注,有问题可以交流。
完
后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。
江湖偌大,继续闯荡,愿大侠一切安好,有缘再见!