FPGA Fanout-Fanin(扇入扇出)

在谈到多扇出问题之前,先了解几个相关的信息,也可以当成是名词解释。

扇入、扇出系数

扇入系数是指门电路允许的输入端数目。一般门电路的扇入系数为1—5,最多不超过8。扇出系数是指一个门的输出端所驱动同类型门的个数,或称负载能力。一般门电路的扇出系数为8,驱动器的扇出系数可达25。扇出系数体现了门电路的负载能力。

灌电流、拉电流

当逻辑门输出端是低电平时,灌入逻辑门的电流称为灌电流,灌电流越大,输出端的低电平就越高。当逻辑门输出端是高电平时,逻辑门输出端的电流是从逻辑门中流出,这个电流称为拉电流。简单的理解就是逻辑门的输入(灌电流)和输出电流(拉电流)。

上、下拉电阻

上拉电阻就是将不确定的信号通过一个电阻嵌位在高电平(即拉电流),来增加高电平输出时的驱动能力,以解决总线驱动能力不足时提供电流;下拉电阻就是将不确定的信号通过一个电阻嵌位在低电平,是用来吸收电流的,也就是灌电流。

负载效应

当输出负载大于它的扇出能力的时候,就会有如下的效应:
  1、输出低态时,输出电压VOL可能高于VOLmax。
  2、输出高态时,输出电压VOH可能高于VOLmin。
  3、输出的传播延迟可能大于规格说明的延迟值。
  4、输出的上升和下降时间可能大于规格说明的延迟值。
  5、器件工作温度可能升高,从而降低其可靠性,最终引起器件失效。

前面已经介绍了什么是扇出和扇出系数。多扇出问题,通常是指用一个节点驱动多个下级逻辑器件,此问题会严重影响FPGA布线的稳定性,设计的时候要多加注意,此时采用的是复制寄存器策略。
  举个例子: CLK为系统时钟,M为1MHz方波信号,由于M信号驱动的模块较多,所以M的扇出较多,为了减少扇出,用系统时钟采样,将M信号驱动7个D触发器,然后将7个D触发器的输出端分给7个模块,这样每个复制点(DUP0~DUP6)平均扇出变为原来的1/7,M的信号扇出变为7,这样就减少了每个信号的扇出,优化了逻辑,也提高了设计的整体性能。简而言之,就是将一路信号用D触发器和CLK将其分成两路信号,或者是更多路的信号,再让这些信号来驱动下面的各个模块。

如何判断是系统的问题是由于多扇出而导致的呢。我是这样判断的,因为我的模块是一个一个写,一块一块的调试的,所以在分开调试的时候没有出任何问题,当所有的综合在一起的时候,就出现了有一个模块的信号总是不正常,单独调试该模块的时候又正常,然而这两个模块之间又没有什么交互信号,所以,就把问题定位在这个共用信号的驱动能力上了,最终曾加了D触发器,解决了这个问题。
  另外经高手指点,还有一种方法是将这个频率信号放在CLK的管脚上,因为时钟管脚的驱动能力比一般的IO口要大一些,所以也就能够带动更多的逻辑门。
##门控时钟的问题
  最后添加一点关于门控时钟的问题。在编译的时候,系统会报警告,Found X node(s) in clock paths which may be acting as ripple and/or gated cloxks。这种情况是由于使用了门电路来产生时钟,一般处理这个警告的方式都会说:“如果是这样设计的,就不管这个警告了。”不过看了下面的一段文字也就能够理解为什么会有这样的警告了。如果想设计出更完美的产品,还是要考虑这个问题的。
  门控时钟指的是不用FPGA内部的全局时钟资源BUFG来控制触发器的时钟沿输入端而是采用组合逻辑和其它时序逻辑(如分频器)产生的信号作为触发器的时钟沿输入端。门控时钟容易带来时钟漂移、毛刺等,使得触发器误动作,通常,对于驱动的触发器数量较少的门控时钟,编译器可以自动将分布时钟缓冲器将其布线优化,但是对于驱动触发器较多的门控时钟,将会使布线不稳定,重者造成设计混乱。门控时中较多,也会使得整个设计的最大工作速度下降,降低产品的性能。
  对于门控时钟问题,通常的解决办法是将分频器做成与系统时钟宽度一个周期宽度的脉冲信号,所谓系统时钟就是用全局时钟资源BUFG驱动的高扇出、零漂移、零畸变的时钟资源,在FPGA内部的布线结构是树形结构。
将分频器的输出送入触发器的ce端,当系统时钟到来时,检测ce信号的有效性,当ce信号有效时,将触发器的输出改变,和分频器的作用完全一样,而且这样处理也使得布线更加优化。
  参考:http://blog.tianya.cn/blogger/post_read.asp?BlogID=4224308&PostID=41813272

在模块化设计中

模块的扇出是指模块的直属下层模块的个数,如图7.8所示。图7.8中,平均的扇出是2。一般认为,设计得好的系统平均扇出是3或4。
  这里写图片描述
               图7.8模块的扇出

一个模块的扇出数过大或过小都不理想,过大比过小更严重。一般认为扇出的上限不超过7。扇出过大意味着管理模块过于复杂,需要控制和协调过多的下级。解决的办法是适当增加中间层次。

一个模块的扇入是指有多少个上级模块调用它。扇人越大,表示该模块被更多的上级模块共享。这当然是我们所希望的。但是不能为了获得高扇人而不惜代价,例如把彼此无关的功能凑在一起构成一个模块,虽然扇人数高了,但这样的模块内聚程度必然低。这是我们应避免的。

设计得好的系统,上层模块有较高的扇出,下层模块有较高的扇人。其结构图像清真寺的塔,上面尖,中间宽,下面小。

###1.门电路的扇入扇出

扇入系数,是指门电路允许的输入端数目。
  一般TTL电路的扇入系数 Nr为1~5,最多不超过8。若芯片输入端数多于实际要求的数目,可将芯片多余输入端接高电平(+5V)或接低电平(GND)。
  扇出系数,是指一个门的输出端所驱动同类型门的个数,或称负载能力。
  NO=IOLMAX/IILMAX,这是一个通俗的定义一般用在TTL电路的定义中。其中IOLMAX为最大允许灌电流,IILMAX是一个负载门灌入本级的电流。
  TTL电路的扇出系数Nc为8~10。
  CMOS电路的扇出系数Nc可达20~25。
  当然LVTTL和LVCMOS都可进一步验算获得。
  Nc表征了门电路的负载能力。
####1.TTL電路
TTL的验算是比较好弄的,TTL与TTL之间如下图所示:
这里写图片描述
由于本身晶体管的转换速度有限,因此对于TTL来说,扇入扇出系数无所谓低频和高频而言。
####2.CMOS
扇出系数实质上是根据频率有关的。
这里写图片描述

因此,扇出系数是根据输出波形识别的时序而定的,随着频率的增加,扇出系数越来越小。
  这是因为理论上来说Rdson和Ci都是确定的,根据充放电过程
  注意Rdson的能力计算可参考前面的博文
这里写图片描述
  通过计算时间常数,我们可测算
  1.10%=>90%的时间,并确认高电平的时间。
  2.90%=>10%的时间,并确认低电平的时间。
  如果这两个都符合,则可接受。
  当然MOS管的输出电容和PCB板的寄生电感和电容,这些因素都会影响实际的效果。

### FanoutFanin 的概念 Fanout 是指将数据分发到多个通道或目标的过程,而 Fanin 则是指从多个通道接收数据并将其聚合到单个通道中的过程。这两种操作通常用于并发编程场景中,特别是在 Go 语言的 Goroutine 中非常常见。 以下是基于 Go 语言实现 FanoutFanin 的封装方法及其示例代码: --- ### 实现 FanoutFanin 的封装 #### 1. **Fanout 的实现** Fanout 可以通过创建多个 Goroutines 并向它们发送相同的数据流来完成。下面是一个简单的 Fanout 封装函数,其中输入数据被复制到多个输通道上[^4]。 ```go package main import ( "fmt" ) // Fanout 函数定义 func fanout(input <-chan int, outputs []chan<- int) { for value := range input { for _, output := range outputs { output <- value } } closeAll(outputs) } // 关闭所有的输通道 func closeAll(channels []chan<- int) { for _, ch := range channels { close(ch) } } ``` 上述代码展示了如何将一个输入通道 `input` 复制到多个输通道 `outputs` 上。每次接收到新的值时,都会将其广播给所有连接的输通道。 --- #### 2. **Fanin 的实现** Fanin 负责从多个输入通道读取数据并将这些数据合并成单一的输通道。以下是一个 Fanin 的简单实现[^5]: ```go // Fanin 函数定义 func fanin(inputs ...<-chan int) chan int { var wg sync.WaitGroup out := make(chan int) output := func(c <-chan int) { defer wg.Done() for n := range c { out <- n } } wg.Add(len(inputs)) for _, in := range inputs { go output(in) } go func() { wg.Wait() close(out) }() return out } ``` 在这个例子中,`fanin` 接收任意数量的输入通道,并启动一个新的 Goroutine 来监听每个输入通道上的数据。当某个输入通道关闭时,对应的 Goroutine 自动退。最终,在所有输入通道都关闭之后,整个 Fanin 过程也会结束。 --- ### 完整示例代码 以下是一份完整的示例代码,演示了如何组合使用 FanoutFanin: ```go package main import ( "fmt" "sync" ) // Fanout 函数定义 func fanout(input <-chan int, outputs []chan<- int) { for value := range input { for _, output := range outputs { output <- value } } closeAll(outputs) } // 关闭所有的输通道 func closeAll(channels []chan<- int) { for _, ch := range channels { close(ch) } } // Fanin 函数定义 func fanin(inputs ...<-chan int) chan int { var wg sync.WaitGroup out := make(chan int) output := func(c <-chan int) { defer wg.Done() for n := range c { out <- n } } wg.Add(len(inputs)) for _, in := range inputs { go output(in) } go func() { wg.Wait() close(out) }() return out } func main() { input := make(chan int) ch1 := make(chan int) ch2 := make(chan int) // 使用 Fanout 分发数据 fanout(input, []chan<-int{ch1, ch2}) // 合并两个通道的结果 result := fanin(ch1, ch2) // 发送数据到输入通道 go func() { for i := 0; i < 10; i++ { input <- i } close(input) }() // 打印结果 for v := range result { fmt.Println(v) } } ``` --- ### 总结 以上实现了 FanoutFanin 的基本功能。Fanout 主要负责将数据分发到多个消费者,而 Fanin 则专注于收集来自不同生产者的多路数据流并统一处理。这种设计非常适合高并发环境下的任务分配与汇总需求。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OpenFPGA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值