使用 scala 设计过滤器 - For 循环展开
Designing a filter using scala - For loop unrolling
我正在尝试使用 Chisel 创建一个可合成的 FIR 滤波器。我正在从 csv 文件中读取有符号整数,然后将其作为系数传递给 FIR 滤波器 class。只是为了给大家一个味道 - 这就是系数的样子:
-152651856 1233223 ...
然后使用 for 循环对滤波器的系数执行 MAC 操作。我找不到 for 循环未正确展开的原因。它只是将最后一个系数合成为常数。
class FIR (val coefficients:Array[Int], val count:Int ) extends Module {
val io = IO(new Bundle {
val X = Input(Vec(count,SInt(16.W)))
val Y_out = Output(SInt(64.W))
})
val sum = Reg(SInt(64.W))
sum := 0.S(64.W)
val temp = Reg(Vec(count,SInt(64.W)))
for(ct <- 0 until count ) {
temp(ct) := new fromIntToLiteral(coefficients(ct): Int).S
sum := sum + temp(ct) * io.X(ct)
}
io.Y_out := sum
}
这里的数组coefficients
是传递给class的过滤器的系数,count
是系数的数量。
要牢记的重要一点是,Chisel 不是 行为综合。没有阻塞分配(尽管可以模拟)。 Chisel 取而代之的是 last connect semantics 这样最后一个连接就获胜了。这主要在以后的连接被预测时有用(即由 when
保护),但也可用于覆盖默认值。
要查看发生了什么,让我们为 count
选择一个值并展开循环:
// Let count = 4
temp(0) := new fromIntToLiteral(coefficients(0): Int).S
sum := sum + temp(0) * io.X(0)
temp(1) := new fromIntToLiteral(coefficients(1): Int).S
sum := sum + temp(1) * io.X(1)
temp(2) := new fromIntToLiteral(coefficients(2): Int).S
sum := sum + temp(2) * io.X(2)
temp(3) := new fromIntToLiteral(coefficients(3): Int).S
sum := sum + temp(3) * io.X(3)
上次连接使其与以下内容相同:
temp(0) := new fromIntToLiteral(coefficients(0): Int).S
// sum := sum + temp(0) * io.X(0)
temp(1) := new fromIntToLiteral(coefficients(1): Int).S
// sum := sum + temp(1) * io.X(1)
temp(2) := new fromIntToLiteral(coefficients(2): Int).S
// sum := sum + temp(2) * io.X(2)
temp(3) := new fromIntToLiteral(coefficients(3): Int).S
sum := sum + temp(3) * io.X(3) // <--- Winning connect to sum
您可能想做的是在 for 循环执行时逐步创建总和,就像在 Verilog 中使用阻塞赋值一样。我们可以模拟这种行为,使用var
(一个我们可以重新分配的变量)来保存中间值:
val sum = Reg(SInt(64.W))
val temp = Reg(Vec(count,SInt(64.W)))
var sumVar = 0.S(64.W)
for(ct <- 0 until count ) {
temp(ct) := coefficients(ct).S
sumVar = sumVar + temp(ct) * io.X(ct)
}
sum := sumVar // Connect the finished logic to the sum's "next" value.
io.Y_out := sum // Connect current value of sum register to output
我想在这里强调几点:
注意我们用=
代替sumVar
,把它想象成一个指向硬件节点的指针。我们正在更新指针以指向我们在每次迭代中构建的新逻辑
我可能误解了您的意图,因为我的代码看起来不像我熟悉的 FIR(编辑:代码正确,请参阅对此答案的评论)。请注意,在 Chisel 中,Reg
对应于一个实际的、寄存器或触发器数组。它 不像 Verilog reg
。 Reg
在此处进行了描述:https://www.chisel-lang.org/chisel3/sequential-circuits.html. You can also take a look at the Chisel Bootcamp 始终使用 FIR 滤波器作为激励示例。
我注意到您直接使用 fromIntToLiteral
。这是一个 隐式 class,实际上不应该面向用户。你可以只写 coefficients(ct).S
,只要你写 import chisel3._
,Scala 就会在你不注意的情况下让它工作。如果您想了解更多细节,隐含的是如何在 Scala 中实现 Extension Methods 。当你 import chisel3._
时,.S
方法隐式地来自 fromIntToLiteral
并使它 看起来 就像 S
是在 [=28 上定义的方法=].把它想象成语法糖,让 Chisel 代码更方便编写。
我正在尝试使用 Chisel 创建一个可合成的 FIR 滤波器。我正在从 csv 文件中读取有符号整数,然后将其作为系数传递给 FIR 滤波器 class。只是为了给大家一个味道 - 这就是系数的样子:
-152651856 1233223 ...
然后使用 for 循环对滤波器的系数执行 MAC 操作。我找不到 for 循环未正确展开的原因。它只是将最后一个系数合成为常数。
class FIR (val coefficients:Array[Int], val count:Int ) extends Module {
val io = IO(new Bundle {
val X = Input(Vec(count,SInt(16.W)))
val Y_out = Output(SInt(64.W))
})
val sum = Reg(SInt(64.W))
sum := 0.S(64.W)
val temp = Reg(Vec(count,SInt(64.W)))
for(ct <- 0 until count ) {
temp(ct) := new fromIntToLiteral(coefficients(ct): Int).S
sum := sum + temp(ct) * io.X(ct)
}
io.Y_out := sum
}
这里的数组coefficients
是传递给class的过滤器的系数,count
是系数的数量。
要牢记的重要一点是,Chisel 不是 行为综合。没有阻塞分配(尽管可以模拟)。 Chisel 取而代之的是 last connect semantics 这样最后一个连接就获胜了。这主要在以后的连接被预测时有用(即由 when
保护),但也可用于覆盖默认值。
要查看发生了什么,让我们为 count
选择一个值并展开循环:
// Let count = 4
temp(0) := new fromIntToLiteral(coefficients(0): Int).S
sum := sum + temp(0) * io.X(0)
temp(1) := new fromIntToLiteral(coefficients(1): Int).S
sum := sum + temp(1) * io.X(1)
temp(2) := new fromIntToLiteral(coefficients(2): Int).S
sum := sum + temp(2) * io.X(2)
temp(3) := new fromIntToLiteral(coefficients(3): Int).S
sum := sum + temp(3) * io.X(3)
上次连接使其与以下内容相同:
temp(0) := new fromIntToLiteral(coefficients(0): Int).S
// sum := sum + temp(0) * io.X(0)
temp(1) := new fromIntToLiteral(coefficients(1): Int).S
// sum := sum + temp(1) * io.X(1)
temp(2) := new fromIntToLiteral(coefficients(2): Int).S
// sum := sum + temp(2) * io.X(2)
temp(3) := new fromIntToLiteral(coefficients(3): Int).S
sum := sum + temp(3) * io.X(3) // <--- Winning connect to sum
您可能想做的是在 for 循环执行时逐步创建总和,就像在 Verilog 中使用阻塞赋值一样。我们可以模拟这种行为,使用var
(一个我们可以重新分配的变量)来保存中间值:
val sum = Reg(SInt(64.W))
val temp = Reg(Vec(count,SInt(64.W)))
var sumVar = 0.S(64.W)
for(ct <- 0 until count ) {
temp(ct) := coefficients(ct).S
sumVar = sumVar + temp(ct) * io.X(ct)
}
sum := sumVar // Connect the finished logic to the sum's "next" value.
io.Y_out := sum // Connect current value of sum register to output
我想在这里强调几点:
注意我们用
=
代替sumVar
,把它想象成一个指向硬件节点的指针。我们正在更新指针以指向我们在每次迭代中构建的新逻辑我可能误解了您的意图,因为我的代码看起来不像我熟悉的 FIR(编辑:代码正确,请参阅对此答案的评论)。请注意,在 Chisel 中,
Reg
对应于一个实际的、寄存器或触发器数组。它 不像 Verilogreg
。Reg
在此处进行了描述:https://www.chisel-lang.org/chisel3/sequential-circuits.html. You can also take a look at the Chisel Bootcamp 始终使用 FIR 滤波器作为激励示例。我注意到您直接使用
fromIntToLiteral
。这是一个 隐式 class,实际上不应该面向用户。你可以只写coefficients(ct).S
,只要你写import chisel3._
,Scala 就会在你不注意的情况下让它工作。如果您想了解更多细节,隐含的是如何在 Scala 中实现 Extension Methods 。当你import chisel3._
时,.S
方法隐式地来自fromIntToLiteral
并使它 看起来 就像S
是在 [=28 上定义的方法=].把它想象成语法糖,让 Chisel 代码更方便编写。