写在前面

学过一门或多门软件语言的数字设计初学者经常会犯一些错误 ,例如硬件语言的并发性,可综合以及不可综合语句区分,循环语句的使用等等。本文的建议将带你区别并扫除这些易错点,助你成为一名优秀的硬件设计师。


正文



可综合以及不可综合的代码

了解如何编写可在FPGA或ASIC上运行的代码?

当您编写Verilog或VHDL代码时,您正在编写将被转换为门,寄存器,RAM等的代码。执行此任务的程序称为综合工具。综合工具的工作是将您的Verilog或VHDL代码转换为FPGA可以理解的代码。但是,Verilog和VHDL的某些部分FPGA根本无法实现。当您这样编写代码时,它称为不可综合的代码。

那么,为什么您要使用一种语言,该语言包含无法综合的代码?原因是它使您的测试平台功能更强大。当您编写用于仿真的测试平台时,通常使用不可合成的代码结构会使您的测试平台更好,并使您更轻松地完成工作。

延迟声明

最基本的不可合成代码是延迟语句。 FPGA没有时间概念,因此不可能告诉FPGA等待10纳秒。 相反,您需要使用时钟和触发器来实现您的目标。 下面是一个不可合成代码的示例,该代码已被转换为可以由工具合成的代码。

-- Non-Synthesizable Delay Statement:
r_Enable <= '0';
wait for 100 ns;
r_Enable <= '1';
-- Converted to Synthesizable code (assuming 100 MHz clock):
always@(posedge clk) begin
	if( index < 10 ) begin
		index <= index + 1;
		r_Enable <= 0;
	end
	else begin
		r_Enable <= 1;
		index <= 0;
	end
end

循环语句
数字设计初学者经常滥用的另一段代码是循环语句,例如while,for,repeat等。可综合代码中的循环实际上无法像在C等软件语言中那样使用。 硬件开发初学者面临的巨大问题是, 他们已经在C语言中看到了数百次循环,因此他们认为在Verilog和VHDL中它们是相同的。 在这里让我清楚:循环在硬件中的行为与在软件中的行为不同。 在您了解循环语句如何工作之前,您不应该使用它们。

知道综合和不可综合代码之间的区别对于成为一名优秀的数字设计师非常重要。 仅在编写将在FPGA上运行的代码时使用可综合的构造!

每个软件程序员需要了解的有关硬件设计的内容

对于数字设计新手而言最重要的部分

尝试使用VHDL或Verilog进行编程的每个了解C或Java语言的软件开发人员都会遇到相同的问题。他们对代码的工作原理进行了假设。这些假设通常适用于所有软件语言。不幸的是,这些假设不适用于硬件描述语言。如果您不熟悉硬件开发,但懂一种或两种软件语言,请先阅读本文提供了代码示例,并解释了代码在软件世界和硬件世界中如何工作,以向您展示它们之间的区别。

假设1:串行与并行逻辑

这可能是硬件和软件编程语言之间最根本的区别。软件设计师仅见过串行代码,但他们可能没有意识到这一事实。串行代码的意思是代码行一次执行一行。例如,第2行只能在第1行完成后才能执行。VHDL和Verilog不会这样!它们被称为并行逻辑语言,所有代码行都可以并且将同时执行。这称为并发。这是演示串行和并行逻辑之间区别的示例。假设一位设计师希望每十个时钟点亮一次LED。

示例软件代码:

while (1)
{
  if (count == 9)
  {   
    LED_on = 1;
    count = 0;
  }
  else
  {
    LED_on = 0;
    count = count + 1;
  }
}

与其等价的Verilog代码为:

always@(posedge clk ) begin
	if(count < 9) begin
		count <= count + 1;
	end
	else begin
		count <= 0;
	end
end

assign LED_on = (count == 9) ? 1 : 0;

VHDL代码为:

P_INCREMENT : process (clock)
begin
  if rising_edge(clock) then
    if (count < 9) then
      count <= count + 1;
    else
      count <= 0;            
    end if;
  end if;
end process P_INCREMENT;
 
LED_on <= ‘1’ when count = 9 else ‘0’;

这里要意识到的重要一点是,在软件代码中,每行都将执行,然后允许下一行执行。在VHDL和Verilog中并非如此,这在分配LED_on信号的最后一行中得到了证明。该行与VHDL进程同时运行。它始终为LED_on分配“ 1”或“ 0”。如果这是软件,则只有在执行了前面的代码行后才能到达此行。优秀的数字设计师需要始终记住VHDL和Verilog是并行语言。

假设2:循环

这是新硬件开发人员面临的一个巨大问题。他们已经在C语言中看到了数百次循环,因此他们认为在Verilog和VHDL中它们是相同的。在这里让我清楚:for循环在硬件和软件中的行为不同。在您了解for循环如何工作之前,您不应该使用它们。

示例软件代码:

for (int i=0; i<10; i++)
  data[i] = data[i] + 1;

此代码将获取数组“ data”中的每个值并将其递增1。
等效为Verilog代码为:

	//reg [3 : 0] index = 0; 
	always@(posedge clk) begin
		if(index < 10) begin
			data[index] <= data[index] + 1;
			index <= index + 1;
		end
		
	end

这是VHDL中的等效代码:

P_INCREMENT : process (clock)
begin
  if rising_edge(clock) then
    if (index < 10) then
      data[index] <= data[index] + 1;
      index <= index + 1;
    end if;
  end if;
end process P_INCREMENT;

开始在这里看到一个模式?用C编写的代码几乎可以减少与VHDL或Verilog中的代码类似的功能。我要大胆地说一下:如果您至少没有做过3种FPGA设计,则永远不要使用for循环。因此,请考虑如何重写软件中编写的代码,以使其永远不使用for循环。通常,您所需要做的就是添加一个计数器信号(例如上例中的index),以完成与for循环相同的操作。

当然,如果你不是一个数字设计新手,你可以使用可综合的for循环

always@(posedge clk) begin : for_
	for(integer index = 0; index < 10; index = index + 1) begin
		data[index] <= data[index] + 1;
	end
end

假设3:立即执行代码

这与上面的串行与并行逻辑讨论有关,但这是软件开发人员经常犯的一个常见错误。软件开发人员需要记住,不会立即执行一行代码,并且会更新信号值以供使用。下面的示例详细说明了状态机的这一假设。下面的示例显示了某些外围组件(例如模数转换器)的初始化例程。FPGA需要向ADC写入数据:数据线上的5、6、1、0。软件中描述的状态机将允许数据值随状态变化而变化。不幸的是,在下面的硬件代码示例中,情况并非如此:

示例软件代码:

state = INITIALIZE;
data = 5;
state = LOAD_1;
data = 6;
state = LOAD_2;
data = 1;
state = DONE;
data = 0;

新手容易等价为(错的):

P_STATE_MACHINE : process (clock)
begin
  if rising_edge(clock) then
    state <= INITIALIZE;
    data <= 5;
    state <= LOAD_1;
    data <= 6;
    state <= LOAD_2;
    data <= 1;
    state <= DONE;
    data <= 0;
  end if;
end process P_STATE_MACHINE;

或(错的):

always@(posedge clk) begin
	state <= INITIALIZE;
    data <= 5;
    state <= LOAD_1;
    data <= 6;
    state <= LOAD_2;
    data <= 1;
    state <= DONE;
    data <= 0;
end

此代码的硬件表示形式非常糟糕!(多驱动)由于进程中的每一行同时执行,因此数据将始终停留在0且状态将始终停留在DONE(仿真)。此代码永远不会像在软件代码中那样执行状态机。下面的代码是用VHDL编写此状态机的正确方法:

等效的硬件代码应为:

P_STATE_MACHINE : process (clock)
begin
  if rising_edge(clock) then
    if (state == INITIALIZE) then
      data <= 5;
      state <= LOAD_1;
    elsif (state == LOAD_1) then
      data <= 6;
      state <= LOAD_2;
    elsif (state == LOAD_2) then
      data <= 1;
      state <= DONE;
    elsif (state == DONE) then
      data = 0;
    else
      state <= INITIALIZE;
    end if;
  end if;
end process P_STATE_MACHINE;

或:

always@(posedge clk)  begin
	case(state)
		INITIALIZE: begin
			state <= LOAD_1;
			data <= 5;
		end
		LOAD_1: begin
			state <= LOAD_2;
			data <= 6;
		end
		LOAD_2: begin
			state <= DONE;
			data <= 1;
		end
		DONE: begin
			data <= 0;
		end
		defeault: begin
			state <= INITIALIZE;
		end

	endcase
end

换成if else结构也行,对功能不影响。

上面的三个例子是在VHDL或Verilog中开始新设计时,新软件开发人员经常会遇到困难的三个地方。上面的三个假设应该始终是新的数字设计师想到的。应该始终考虑有关并发性,for循环和代码执行的问题。一旦完全理解了上面的示例,你将很快成为成功的数字设计师!



交个朋友

  • 个人微信公众号:FPGA LAB
    FPGA基础知识极简教程(2)抛却软件思维去设计硬件电路_循环语句