异步FIFO为什么要使用格雷码

首先要了解的是异步FIFO 使用格雷码的唯一目的就是“即使在亚稳态进行读写指针抽样也能进行正确的空满状态判断”。


那么典型的判断方法是怎样进行的呢?以满状态判断为例,先要对读指针(属于读时钟域)在写时钟域进行抽样,然后才能与写指针进行比较,如果写指针赶上了读指针,说明已经写满,写操作必须暂停。


接下来说说为什么 2 进制指针不适合做空满判断。事实上 2 进制读指针在增减时,经常发生多位突变,比如 6 位地址 111111 会在下一时刻变成 000000 ,在实际电路中,这个变化过程要持续很长一段时间,会由 111111 经历 6 个状态转移到达 000000 。比如 111111-> 101111 -> 100111 ->100110 -> 100100 -> 000100-> 000000 。由于写时钟与读时钟不同步,异步的写时钟很可能会在状态不稳定的中间某个状态抽样,这样就会得到错误的读指针,进而做出错误的状态判断,导致系统异常。而且由于多位同时突变,凭借概率论常识可知发生错误的可能性很大。


那么怎样才能避免这个问题的发生呢?显然,在中间状态抽样,这个是不可避免的,这是异步系统天生的缺陷。我们的目标是:即使在中间状态抽样,也要不影响空满状态的判断。符合这个要求的编码方法是每次只能有 1 个比特发生改变。为什么这么说呢?因为当只有一个比特发生改变时,即使在中间状态抽样,其结果也不外乎两种:递增前原指针和递增后新指针。显然递增后新指针是最新情况的反映,如果抽样到这个指针,那么和我们的设计预期是一致的,如果抽样到递增前的原指针,会有什么结果呢?假设现在抽样读指针,那么最坏的情况就是把“不满”判断成了“满”,使得本来被允许的写操作被禁止了,但是这并不会对逻辑产生影响,只是带来了写操作的延迟。同样的,如果现在抽样写指针,那么 最坏的情况就是把“不空”判断成了“空”,使得本来被允许的读操作被禁止了,但是这也不会对逻辑产生影响,只是带来了读操作的延迟。

显然每次只变化1比特的编码方案可以有效解决中间状态下空满状态的判断问题,格雷码就是这样的一种编码。
 
回答一:
 
格雷码是的确非常有用。它的特点就是相邻码只有一位发生翻转,比如00->01->11->10->00...
--------------------------------------
在编写状态机时,我也喜欢用这种相邻状态只发生一位翻转的编码。这样从一个状态切换到下一个状态,就只有一位寄存器发生变化。这么做可以带来降低功耗的好处,因为寄存器的翻转是消耗动态功耗的,而这种编码将翻转的次数降到最低,所有有利于降低功耗。
 
回答二:
 
你说的降低功耗的前提是,一个状态到下一个状态的变化也要遵循格雷码连续的变化,否则,就不成立了。比如:如你所说,00<->01<->11<->10这样的变化,确实是有利于降低功耗;但是,状态的变化不一定就是这样变化,它有可能是00<->11,01<->10这样的变化,那么你说的有利于降低功耗就不成立。
 
回答三:
 
低功耗设计的内容还是很博大精深的。如何合理的选择信号形式,以使得系统在发生状态变化时只有少数信号发生翻转?所依赖的公式为

P=a*C*V^2*f


其中a为信号翻转率,C为系统等效电容,V为电路驱动电压,f为时钟频率。使用格雷码降低功耗就是想使a小下来。当然如果电路中做不到严格的格雷码也没关系,只要是有利于减小a的,就有利于功耗的降低。
---------------------------------------------------
期待牛人能对低功耗设计的内容普及一下。在移动的智能终端上低功耗的问题已经越来越受重视了。
回答四:
 
主要是为了多比特同步,这个例子中功耗是次要的因素
 
回答五:
 
格雷码有两个作用,一是消除多个比特同时变化带来的潜在竞争与冒险,二是降低功耗(翻转次数减少)。在状态机中通常为了简单起见表示现态与次态的状态参数并不使用格雷码而是二进制码,因为一个状态的变化可能是发散的而非单向的,就是说不同的条件对应不同的次态,这个时候用格雷码对状态进行编码达不到一次只变化一位的目的,除非在特殊情况下,即状态机的变化是单向的,总是由S0到S1,再到S2,等等,这个时候用格雷码就能达到原来的目的。
 
回答六:
 
十进制计数容易产生毛刺,格雷码则不会产生毛刺。
 
回答七:
 
格雷码每次只有一位跳变,两级寄存才安全。
 
回答八:
 
在异步的FIFO中,采用格雷码进行计数,相邻的数据仅仅只有1bit变化,这样在两个时钟域同步的时候仅仅可能只有1bit产生亚稳态,通过同步以后,亚稳态可以消除,最坏的情况是这1bit采错,但是即使是采错地址也只是相差1个,这对判断空满标志不会产生影响。
   如果是采用10十进制进行编码,则相邻的数据可能有很多位同时进行变化,那么如果多位同时产生亚稳态而且同时采错数据,会对寄存器的空满标志做出严重错误的判断,会丢失数据或者读出无用的数据,使系统出错。
 
回答九:
 
楼上说的都很精彩!顺便补充一点:

由于跳变减少,可减少功耗,多用于低功耗设计中。
 
回答十:
 
我在想这个问题,
如果单指gray的话确实如此
但是在实际应用中,往往是将二进制的转换成gray
就是说不管如何二进制的数依然会存在
同时其转换的外加逻辑必然会带来损耗,
在多比特的情况,功耗是否会跟多呢
gray一般是用在不同时钟域的连续数值的传送,即数据必须是递增或递减的
这样,其主要是在asynchronou fifo中使用
感觉功耗并不会下降
DQN(Deep Q-Network)是一种使用深度神经网络实现的强化学习算法,用于解决离散动作空间的问题。在PyTorch中实现DQN可以分为以下几个步骤: 1. 定义神经网络:使用PyTorch定义一个包含多个全连接层的神经网络,输入为状态空间的维度,输出为动作空间的维度。 ```python import torch.nn as nn import torch.nn.functional as F class QNet(nn.Module): def __init__(self, state_dim, action_dim): super(QNet, self).__init__() self.fc1 = nn.Linear(state_dim, 64) self.fc2 = nn.Linear(64, 64) self.fc3 = nn.Linear(64, action_dim) def forward(self, x): x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x ``` 2. 定义经验回放缓存:包含多条经验,每条经验包含一个状态、一个动作、一个奖励和下一个状态。 ```python import random class ReplayBuffer(object): def __init__(self, max_size): self.buffer = [] self.max_size = max_size def push(self, state, action, reward, next_state): if len(self.buffer) < self.max_size: self.buffer.append((state, action, reward, next_state)) else: self.buffer.pop(0) self.buffer.append((state, action, reward, next_state)) def sample(self, batch_size): state, action, reward, next_state = zip(*random.sample(self.buffer, batch_size)) return torch.stack(state), torch.tensor(action), torch.tensor(reward), torch.stack(next_state) ``` 3. 定义DQN算法:使用PyTorch定义DQN算法,包含训练和预测两个方法。 ```python class DQN(object): def __init__(self, state_dim, action_dim, gamma, epsilon, lr): self.qnet = QNet(state_dim, action_dim) self.target_qnet = QNet(state_dim, action_dim) self.gamma = gamma self.epsilon = epsilon self.lr = lr self.optimizer = torch.optim.Adam(self.qnet.parameters(), lr=self.lr) self.buffer = ReplayBuffer(100000) self.loss_fn = nn.MSELoss() def act(self, state): if random.random() < self.epsilon: return random.randint(0, action_dim - 1) else: with torch.no_grad(): q_values = self.qnet(state) return q_values.argmax().item() def train(self, batch_size): state, action, reward, next_state = self.buffer.sample(batch_size) q_values = self.qnet(state).gather(1, action.unsqueeze(1)).squeeze(1) target_q_values = self.target_qnet(next_state).max(1)[0].detach() expected_q_values = reward + self.gamma * target_q_values loss = self.loss_fn(q_values, expected_q_values) self.optimizer.zero_grad() loss.backward() self.optimizer.step() def update_target_qnet(self): self.target_qnet.load_state_dict(self.qnet.state_dict()) ``` 4. 训练模型:使用DQN算法进行训练,并更新目标Q网络。 ```python dqn = DQN(state_dim, action_dim, gamma=0.99, epsilon=1.0, lr=0.001) for episode in range(num_episodes): state = env.reset() total_reward = 0 for step in range(max_steps): action = dqn.act(torch.tensor(state, dtype=torch.float32)) next_state, reward, done, _ = env.step(action) dqn.buffer.push(torch.tensor(state, dtype=torch.float32), action, reward, torch.tensor(next_state, dtype=torch.float32)) state = next_state total_reward += reward if len(dqn.buffer.buffer) > batch_size: dqn.train(batch_size) if step % target_update == 0: dqn.update_target_qnet() if done: break dqn.epsilon = max(0.01, dqn.epsilon * 0.995) ``` 5. 测试模型:使用训练好的模型进行测试。 ```python total_reward = 0 state = env.reset() while True: action = dqn.act(torch.tensor(state, dtype=torch.float32)) next_state, reward, done, _ = env.step(action) state = next_state total_reward += reward if done: break print("Total reward: {}".format(total_reward)) ``` 以上就是在PyTorch中实现DQN强化学习的基本步骤。需要注意的是,DQN算法中还有很多细节和超参数需要调整,具体实现过程需要根据具体问题进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值