Haskell 时钟速率的术语级访问

Haskell 时钟速率的术语级访问,haskell,type-level-computation,singleton-type,clash,Haskell,Type Level Computation,Singleton Type,Clash,我想访问时钟频率(以Hz为单位)作为术语级值,以便在计数器中使用它 到目前为止,我能想到的一种方法是将类型级别Domain解包为其时钟周期(ps),然后将其转换为时钟速率。但是,这需要一个额外的KnownNat ps约束,该约束将感染所有试图使用它的人,一直到顶级: clkPeriod :: forall dom gated name ps. (dom ~ Dom name ps, KnownNat ps) => Clock dom gated -> Integer clkPerio

我想访问时钟频率(以Hz为单位)作为术语级值,以便在计数器中使用它

到目前为止,我能想到的一种方法是将类型级别
Dom
ain解包为其时钟周期(ps),然后将其转换为时钟速率。但是,这需要一个额外的
KnownNat ps
约束,该约束将感染所有试图使用它的人,一直到
顶级

clkPeriod :: forall dom gated name ps. (dom ~ Dom name ps, KnownNat ps) => Clock dom gated -> Integer
clkPeriod clk = natVal (Proxy :: Proxy ps)

clkRate :: (dom ~ Dom name ps, KnownNat ps) => Clock dom gated -> Integer
clkRate clk = 10^12 `div` clkPeriod clk
另一种避免引入额外
KnownNat
约束的方法是导入
Clash.Signal.Internal
时钟上的模式匹配,因为它包含一个
SNat
周期见证:

import Clash.Signal.Internal (Clock(..))

clkPeriod :: Clock dom gated -> Integer
clkPeriod (Clock _ period) = snatToInteger period
clkPeriod (GatedClock _ period _) = snatToInteger period

clkRate :: Clock dom gated -> Integer
clkRate clk = 10^12 `div` clkPeriod clk
但这会使合成器崩溃 (我想我不应该导入
Clash.Signal.Internal
):


我的问题是,有没有一种方法可以在术语级别具体化
clkRate
,而不引入任何额外的
KnownNat
约束,也不导入任何
内部
模块?

看来,Clash主要开发人员Christiaan Baaij已经承认这是中的Clash编译器错误。引用他在该问题报告中的评论:

所以问题是,我们得到的核心是在
Integer
类型的东西上进行模式匹配,并公开其表示的内部;编译器显然不能处理的东西。我将创建一个变通方法/hack,以始终选择
整数
位于[-2^63..2^63-1]范围内的分支,因为Clash当前“错误地”将
整数
转换为64位数字


使用Clash 1.0.0,可以在不受爬行
KnownNat
约束或不导入任何
内部
模块的情况下完成此操作。诀窍是使用
knownDomain
获取给定时钟域的
SDomainConfiguration
,该域将包含单个时钟周期长度(皮秒)的
SNat周期。通过在
SNat
构造函数上进行模式匹配,将
KnownNat
见证引入范围

clkPeriod :: forall dom. (KnownDomain dom) => Clock dom -> Integer
clkPeriod clk = case knownDomain @dom of
    SDomainConfiguration _ rate@SNat{} _ _ _ _ -> natVal rate

clkRate :: (KnownDomain dom) => Clock dom -> Integer
clkRate clk = 1_000_000_000_000 `div` clkPeriod clk
以下是更新的顶级定义:

{-# LANGUAGE NumericUnderscores #-}
module Test where

import Clash.Prelude hiding (clkPeriod)
import Data.Word

createDomain vSystem{vName="Dom25", vPeriod = hzToPeriod 25_000_000}

topEntity
    :: Clock Dom25
    -> Reset Dom25
    -> Enable Dom25
    -> Signal Dom25 Bit
topEntity = exposeClockResetEnable board
  where
    board = boolToBit <$> r

    r = regEn False (counter .==. 0) (not <$> r)
    counter = register clkrt $ mux (counter .==. 0) (pure clkrt) (pred <$> counter)
    clkrt = fromInteger @Word32 $ hideClock clkRate
{-#语言数字下划线}
模块测试在哪里
导入冲突。前奏隐藏(clkPeriod)
导入数据.Word
createDomain vSystem{vName=“Dom25”,vPeriod=hzOperatiod 25_000_000}
顶级实体
::时钟Dom25
->重置Dom25
->启用Dom25
->信号Dom25位
topEntity=exposeClockResetEnable板
哪里
board=布尔比特r
r=重新生成错误(计数器=.0)(非r)
计数器=寄存器clkrt$mux(计数器=.0)(纯clkrt)(pred计数器)
clkrt=fromInteger@Word32$hidelock clkRate

我已经测试过,这适用于合成,并且将周期长度转换为时钟频率的除法是在编译时完成的。

您是否有生成异常的自包含最小示例?否则,很难知道潜在的解决方案是否比您已经尝试过的更好。@K.a.Buhr:我还没有一个最小的例子;现实生活中自给自足的例子就是目前的例子。但是,我认为可以通过
hidelock
:@K.A.Buhr访问的
Clock
参数有一些根本性的缺陷。我已经编辑了我的问题,以包含一个最小的示例。
clkPeriod :: forall dom. (KnownDomain dom) => Clock dom -> Integer
clkPeriod clk = case knownDomain @dom of
    SDomainConfiguration _ rate@SNat{} _ _ _ _ -> natVal rate

clkRate :: (KnownDomain dom) => Clock dom -> Integer
clkRate clk = 1_000_000_000_000 `div` clkPeriod clk
{-# LANGUAGE NumericUnderscores #-}
module Test where

import Clash.Prelude hiding (clkPeriod)
import Data.Word

createDomain vSystem{vName="Dom25", vPeriod = hzToPeriod 25_000_000}

topEntity
    :: Clock Dom25
    -> Reset Dom25
    -> Enable Dom25
    -> Signal Dom25 Bit
topEntity = exposeClockResetEnable board
  where
    board = boolToBit <$> r

    r = regEn False (counter .==. 0) (not <$> r)
    counter = register clkrt $ mux (counter .==. 0) (pure clkrt) (pred <$> counter)
    clkrt = fromInteger @Word32 $ hideClock clkRate