Factorio Guide

时序延迟电路 (Clock sequence delay circuit) for Factorio

时序延迟电路 (Clock sequence delay circuit)

Overview

用于延迟时钟序列, 可以让每Tick的信号延迟指定时间后输出

基本说明


这是一个典型的4-bit 60-ticks模块

灵感来源于[tieba.baidu.com] 和这 [tieba.baidu.com]
主旨是通过信号本身去存储时序信息

原理为将信号用二进制方式分割为几个区块
并通过移位的方式将数据从区块中转移, 用以保存时序信息

以4位为一个区块为例:

1-tick S000 0000 0000 0000 0000 0000 0000 1110 14 2-tick S000 0000 0000 0000 0000 0000 1110 0000 224 3-tick S000 0000 0000 0000 0000 1110 0000 0000 3584 4-tick S000 0000 0000 0000 1110 0000 0000 0000 57344 5-tick S000 0000 0000 1110 0000 0000 0000 0000 917504 6-tick S000 0000 1110 0000 0000 0000 0000 0000 14680064 7-tick S000 1110 0000 0000 0000 0000 0000 0000 234881024 move <———————————————-

移位使一个2^4-1以内的信号延迟了7-ticks后输出

  • 模块操作数全部是黄色星号(同时输入多个信号)
  • 需要增加延迟时间的, 可以通过模块串联到需要的倍数
  • 需要增加信号数值上限的, 模块并联到需要的倍数; 输出直接并联, 输入做除法和取模运算
  • 需注意的是, 模块不支持负数输入, 请将输入信号进行标准化

推荐使用的调试模组:

一些示例


1bit 60-ticks 延迟模块


4-bit 60-ticks 延迟模块

7-bit 120-ticks 延迟模块X2 右侧的模块使用更多的运算器但更酷

原理解说

简化流程图

简化模块图

图中值的定义:

T = 延迟总时长
M = 幂值 (每次移动多少位 | 区块位数)
D = 总移位次数 | 区块数量
S = 信号的数值上限
CON = 延时运算器数量 | 额外延迟时间(ticks)

另外:
取高位输出模块取低位模块 必须有相同的延迟时间!
CON = T / D – 2 中的 2 是一个可变值, 为 移位模块1tick + 取低位模块?tick

由于Factorio中信号本质为 32位有符号整数[en.wikipedia.org]. 考虑到符号位的问题, 如果不使用符号位, 则:

S = 2^31-1 (游戏内表示为 2147483647, hex为 0x7FFFFFFF)

如果使用完整的32位包括符号位, 则:

S = 2^32-1 (游戏内表示为 -1, hex为 0xFFFFFFFF) *可以通过windows计算器计算
由于带符号位的值左移, 填充高位的是1而不是0, 所以输出前还需要进行一次对低位的与运算
而经过下面的计算 实际只有M值1,2,4,8时才有可能使用同样数量的运算器并提升延迟时间

得到有效模块的S值与D值需求:

为了确保移位的过程中不溢出, 必须确保 2^(M*D)*(2^M-1) <= S
变换公式为 S/(2^M-1)-2^(M*D) >= 0

以下是一个lua示例程序, 用于列出可用的D值, 其值越高, 相同延迟时间下所需的运算器数量就越少

S= –在这设定S值, 例如: S=2^31-1 M= –在这设定M值, 例如: M=2 for i=1,32,1 do Dv=S/(2^M-1)-2^(M*i); if Dv >= 0 then print(“D: ” .. i) end end

*文章末提供用于计算的xlsx表, 可以自行根据需求进行计算

Let’s do it

一个31位上限的有效示例
N
M
D
Out
Loop
CON
T
2
1
30
1
2
0
60
4
2
14
1
2
0
28
8
3
9
1
2
0
18
16
4
6
1
2
0
12
32
5
5
1
2
0
10
64
6
4
1
2
0
8
128
7
3
1
2
0
6
256
8
2
1
2
0
4
512
9
2
1
2
0
4
1024
10
2
1
2
0
4
2048
11
1
1
2
0
2

一个32位上限的有效示例
N
M
D
Out
Loop
CON
T
2
1
31
2
3
0
93
4
2
15
2
3
0
45
16
4
7
2
3
0
21
256
8
3
2
3
0
9

*注: 一个模块的运算器数量为Out+Loop+CON

图中值的定义:

Out = 用于输出的 取高位运算器 数量
Loop = 移位运算器 和 取低位运算器 数量
T_Uni = 将模块延迟总时长统一 方便计算CP值
EffX = 自定义CON 和 T-Uni 得到所需的 模块数量
EffC = 自定义CON 和 T-Uni 得到所需的 运算器数量

这里提供一个简单的用于计算CON和EffX和EffC的示例:

T_Uni= –在这设定需求的延迟时长, 例如: 120 (2s) D= –在这设定D值, 根据实际情况设定, 较高的值更省运算器 Out= –在这设定需求的Out值, 为 取高位输出模块 实际的延迟时间 Loop= –在这设定需求的Loop值, 为 移位运算器 和 取低位运算器 实际的延迟时间 print(“CON: T EffX EffC”); for i=0,128,1 do T=D*(Loop+i); EffX=T_Uni/T; EffC=EffX*(Out+Loop+i); if math.tointeger(EffX) then print(i..”: “..T..” “..EffX..” “..EffC) end end

*其中math.tointeger函数需要较高的lua版本才能运行

确认制造目标

以最高数字为100, 延迟 120-ticks (2s) 为例:

根据前表, 选择 M=7 也就是 128, D值 则选择 3

设定 T_Uni=120, D=3, Out=1, Loop=2 得出结果

CON:
T
EffX
EffC
0:
6
20
60
2:
12
10
50
3:
15
8
48
6:
24
5
45
8:
30
4
44
18:
60
2
42
38:
120
1
41

本着使用最少运算器的原则, 我们选择使用 CON=38

开始实际制造一个

汇总一下重要参数 M=7, D=3, Out=1, Loop=2, CON=38, EffX=1

根据以下原理图进行构建

移位模块


设定好M值

CON模块


按CON值设定延迟, 这里以38个1-tick运算器为例

取低位模块


根据计算公式 2^(M*D)-1 得出值 2097151

取高位输出模块

根据计算公式 M*D 得出值 21

成品!

一些基础模块[drive.google.com]
*文章末提供用于计算的xlsx表, 可以自行根据需求进行计算

增加延迟时长&提升信号容量

增加延迟时长

这很简单, 只需要选择信号容量相同的模块, 将其首尾串联到需要的时间
例如 4-bit 60-ticks + 4-bit 40-ticks = 4-bit 100ticks

这里以上一章的模块为例:

模块首尾串联, 总时长360-ticks

提升信号容量

通过并联时长相同的模块, 将其并联到需要的容量
例如 60-ticks 15C + 60-ticks 63C = 60ticks 78C

这里以上一章的模块为例:
最高信号容量为127*4=508, 可以继续增加额外的并联模块来提升容量

除法与取模运算
左上方1个加法运算器用于补偿原始信号的延迟
除法运算得到的值将决定启动几个延迟模块
取模得到的值会用于一个单独的延迟模块

开关门
最左侧两对加法运算器分别用于补偿 取模结果 和 原始信号 的延迟
右侧每对运算器分别为 >0启动; >1启动; >2启动; …, 并将信号以127的值输出

模块可以参考[drive.google.com]

嵌套模块

嵌套模块不会节省运算器的数量, 但可以拥有美观的结构

基本原理为将上层模块中已处理的信号还原到原始信号(对信号的每个区块进行还原), 进行延迟后原样返回

以之前的7-bit 120-ticks为例, 嵌套的模块:

  • 由于D值为3, 需单独的3个模块分别处理各个区块的信号
  • 右侧2×3的Split模块用来 取各区的值
  • 值分别为 (2^M-1)*2^M; (2^M-1)*2^2M; (2^M-1)*2^3M — 1-ticks
  • 下方分别移位 M; 2M; 3M 位来还原信号 — 1-ticks
  • 将信号输出到各自对应区块的延迟模块中 — 33-ticks
  • 原进原出, Merge模块将信号分别移位 M; 2M; 3M 位来还原信号 — 1-ticks
  • 用于补足延迟时间的运算器 — 2-ticks

由于过于繁琐, 更详细的内容这里不做阐述了, 有需要可以使用文末提供的xlsx表格

这里提供一个lua示例程序, 用于计算这些

D= –在这设定D值 Out= –在这设定需求的Out值 Loop= –在这设定需求的Loop值 Split= –在这设定需求的Split值, 为Split模块使用的时间 Merge= –在这设定需求的Merge值, 为Merge模块使用的时间 ST_Uni=CON-Split-Merge –嵌套模块实际需要延迟的时间 print(“SCON: ST ST_add EffS”); for i=0,128,1 do ST=D*(Loop+i); ST_add=ST_Uni-ST; S_M_Con=D*(Split+Merge); EffS=ST_add+S_M_Con+D*(Out+Loop+i); if ST_add >= 0 then print(i..”: “..ST..” “..ST_add..” “..EffS) end end

模块可以参考[drive.google.com]
表格与详细内容可以参考[drive.google.com]

SteamSolo.com