4

我正在创建一个库来描述地址线与内存元素的连接。我认为一元 API 可以很好地创建组件(具有标识,以支持镜像),然后在各种受限地址范围内使用这些组件句柄;例如:

(dataIn, ()) = memoryMap _addrOut _dataOut $ do
    rom <- romFromFile (SNat @0x2000) "SpaceInvaders.bin"
    ram <- ram0 (SNat @0x0400)

    from 0x0000 $ connect rom
    from 0x2000 $ connect ram
    from 0x4000 $ connect ram

然而,一些内存组件也有“背板连接”。例如,假设内存映射的 IO 外设具有字节输出。我可以将其表示为返回一对结果的一元操作:内存元素句柄和输出信号;然后将其留给用户以某种方式将其路由到整个内存映射描述的最终结果:

(dataIn, outByte) = memoryMap _addrOut _dataOut $ do
    rom <- romFromFile (SNat @0x0800) "_build/intel8080/image.bin"
    ram <- ram0 (SNat @0x1800)
    (acia, outByte) <- port $ acia inByte outReady

    matchLeft $ do
        from 0x10 $ connect acia
    matchRight $ do
        from 0x0000 $ connect rom
        from 0x0800 $ connect ram

    return outByte

这种方法的问题是它不承认一个简单的实现。在后台,这些组件最终只是Signal addr -> Signal (Maybe dat) -> (Signal dat, Signal backpane)函数,所以要获取backpane信号,我需要将它们应用到addr信号上;这当然需要查看connect句柄上的所有后续调用。这需要一个凌乱的“打结”实现,这会导致下游进一步出现问题,所以我想避免它。

outByte此外,除了在最后返回它们之外,用户实际上无法对这些背板输出做任何事情,例如。因此,如果仅绑定句柄(如上面的示例中的和) rom,并且其他输出......不知何故......神奇......传送到外面,它会更干净。ramacia

我的意思是,与其在这个递归定义的地址行上应用单个内存组件,不如创建一个内存组件可以只返回一些不透明的句柄,在所有调用之后保留要应用的函数connect,然后应用它。不需要递归,没有背板值暴露给用户,等等。但是这些额外的输出最终如何收集呢?

在我写这个问题的时候,一个突然想到的想法是使用分级单子,其中索引是背板输出信号类型的列表;acia例如,创建句柄将通过一个新元素扩展该列表。运行memoryMap器将在内存读取结果旁边返回某种通用产品类型。但我不喜欢这个想法有两个原因:

  • 使用分级单子对用户来说很笨拙,您需要使用它RebindableSyntax来恢复do符号,类型推断不是那么好,而且总体而言向用户解释它只是一种痛苦。

  • 最终的类型memoryMap会令人惊讶,因为它取决于创建带有背板信号的组件的顺序。

4

0 回答 0