3. Verilog基础与量化数据类型:组合逻辑与时序逻辑回顾、定点数表示(Q格式)、浮点数与定点数转换、量化交易中的特殊数据类型
好,咱们直接进入正题。这一节我打算聊聊Verilog里最基础、但也最容易翻车的地方——组合逻辑和时序逻辑的区别,以及量化交易里那些让人头疼的数据类型。你想想看,在FPGA上做高频交易,数据表示不对,策略写得再漂亮也是白搭。
3.1 组合逻辑 vs 时序逻辑:别搞混了
先说组合逻辑。说白了,就是输入一变,输出立马跟着变,没有记忆功能。比如一个简单的加法器:
assign sum = a + b;
a和b一变,sum立刻更新。这在量化交易里用来做实时计算很常见,比如计算买卖价差。
时序逻辑就不一样了。它靠时钟驱动,只在时钟沿更新。我刚开始做FPGA时,总想把所有东西都塞进组合逻辑里,结果时序一塌糊涂。后来才明白,该用寄存器的地方千万别省。
核心区别:
- 组合逻辑:无记忆,输出只依赖当前输入
- 时序逻辑:有记忆,输出依赖当前输入和过去状态
举个例子,在订单簿更新时,我们需要记住上一笔的买卖挂单量。这就是典型的时序逻辑场景:
always @(posedge clk) begin
if (update_valid)
last_bid_qty <= new_bid_qty;
end
嗯,这里要注意:千万别在同一个always块里混用组合和时序逻辑。我见过有人把assign和always @(posedge clk)混在一起写,结果仿真对得上,上板就乱套。
3.2 定点数表示:Q格式
做量化交易,价格和成交量都是小数。FPGA里用浮点数?太慢了。我们一般用定点数,也就是Q格式。
Q格式长什么样?比如Q16.16,意思就是16位整数位,16位小数位。总共32位。我习惯用Q格式来表示价格,因为精度可控,计算也快。
| Q格式 | 总位数 | 整数位 | 小数位 | 表示范围 |
|---|---|---|---|---|
| Q8.8 | 16 | 8 | 8 | -128 ~ 127.996 |
| Q16.16 | 32 | 16 | 16 | -32768 ~ 32767.99998 |
| Q1.31 | 32 | 1 | 31 | -1 ~ 0.9999999995 |
我个人习惯用Q16.16来表示价格。为什么?因为大部分股票价格在0.01到1000之间,16位整数位足够,16位小数位能精确到1/65536,约0.000015,比交易所的最小报价单位还细。
小技巧:在Verilog里做Q格式乘法时,记得移位。比如两个Q16.16数相乘,结果是Q32.32,你得右移16位才能回到Q16.16。我曾经忘了移位,结果算出来的价格差了65536倍,差点把策略搞崩。
3.3 浮点数与定点数转换
有时候我们得从外部接口拿到浮点数,比如从网络包解析出来的价格。这时候就得转成定点数。
转换公式很简单:
定点数 = 浮点数 × 2^小数位数
比如浮点数100.25转成Q16.16:
100.25 × 65536 = 6569984 (十六进制0x643D00)
反过来,定点数转浮点数:
浮点数 = 定点数 / 2^小数位数
在Verilog里,我一般用组合逻辑做转换:
// 浮点数转定点数(假设输入是32位IEEE754)
wire [31:0] float_in;
wire [31:0] fixed_out;
// 这里省略了具体的浮点解析逻辑
// 实际项目中我会调用一个IP核来做
assign fixed_out = float_to_fixed(float_in, 16);
注意:浮点数转定点数时,小心溢出。比如Q16.16最大只能表示32767.99998,如果浮点数超过这个范围,结果就错了。我建议在转换前加一个饱和处理。
3.4 量化交易中的特殊数据类型
做量化交易,有几个数据类型特别重要:时间戳、价格、成交量。每个都有坑。
3.4.1 时间戳
交易所的时间戳精度越来越高。以前是毫秒级,现在纳秒级都很常见。在FPGA里,我一般用64位计数器来记录时间戳,精度1纳秒。
reg [63:0] timestamp_ns;
always @(posedge clk_1GHz) begin
timestamp_ns <= timestamp_ns + 1;
end
嗯,这里要注意:不同交易所的时间基准可能不一样。有的用UTC,有的用本地时间。我建议统一转成UTC纳秒时间戳,方便做跨交易所的延迟分析。
3.4.2 价格
价格用定点数,前面说过了。但有个细节:不同交易所的最小报价单位不一样。比如A股是0.01元,美股是0.0001美元。我建议在系统里统一用一个Q格式,然后在接口层做转换。
我的经验:统一用Q16.16表示价格,然后在每个交易所的适配器里做缩放。这样核心策略代码不用改,只改适配器就行。
3.4.3 成交量
成交量一般是整数,但可能很大。比如某只股票一天成交几亿股。我建议用32位无符号整数,最大能表示42亿,够用了。
但有个坑:累计成交量。如果你要计算一段时间内的累计成交量,小心32位溢出。我一般用64位来存累计值。
reg [63:0] cumulative_volume;
always @(posedge clk) begin
if (trade_valid)
cumulative_volume <= cumulative_volume + trade_volume;
end
3.5 知识体系总览
为了让你更直观地理解这一章的内容,我画了一张图。它把组合逻辑、时序逻辑、定点数、浮点数转换,以及交易中的特殊数据类型串在了一起。你可以把它当作一个快速参考。
这张图把这一章的核心内容串起来了。左边是Verilog基础,右边是量化数据类型,中间是定点数和浮点数的转换。最下面是实际应用。你写代码的时候,可以对照这张图,看看自己卡在哪一层。
避坑指南:我曾经在时间戳处理上吃过亏。当时用了32位计数器,结果系统跑了几个小时就溢出了,导致所有订单的时间戳都乱了。后来改成64位,再也没出过问题。记住:时间戳、累计成交量这种会不断累加的值,一定要用64位。
好了,这一节就到这里。记住:组合逻辑和时序逻辑要分清楚,定点数选好Q格式,时间戳用64位。这些基础打牢了,后面做策略实现才不容易翻车。