Basic knowledge of FPGA----Chapter 3 Section 5 Functional Description-Combination Logic

Section 5 Functional Description - Combination Logic

5.1 Program Statements

5.1.1 assign statement

The assign statement is a continuous assignment statement. Generally, the value of one variable is assigned to another variable without interruption . The two variables are similar to being connected by a wire, which is used as a connection. The basic format of an assign statement is:

assign a = b (logical operator) c ...;

The function of the assign statement belongs to the category of combinational logic , and its scope of application can be summarized as follows:

(1) continuous assignment ;

(2) Connection ;

(3) Assign value to the wire type variable . Wire is a wire network, which is equivalent to the actual connection line. If you want to use assign to connect directly, use the wire type variable. The value of the wire type variable changes at any time.

It should be noted that multiple assign continuous assignment statements are executed independently and in parallel.

5.1.2 The always statement

The always statement is a conditional loop statement, and the execution mechanism is realized by driving an event called a sensitive variable table , which will be described in detail below. The basic format of the always statement is:

always @(sensitive event)begin

program statement

end

always means "always, always", @ is followed by events. The whole always means: when the condition of the sensitive event is met, execute the "program statement" once. Every time a sensitive event is satisfied, the "program statement" is executed once. (When the sensitive condition in the sensitive event changes, execute the content in the always conditional loop statement

image-20211029113455362

The meaning of this program is: When the signal a, signal b or signal d changes, execute the following statement once. When executing this statement, first judge whether the signal sel is 0, and if it is 0, execute the third line of code. If sel is not 0, execute the fifth line of code. It should be emphasized that if any one of a, b, and c changes once, lines 2 to 5 will only be executed once, and will not be executed for the second time.

It should be noted here that only the change of the sel signal will not execute the codes from line 2 to line 5, which is usually not in line with the designer's idea. For example, the general designer's idea is: when sel is 0, the result of c is a+b; when sel is not 0, the result of c is a+d. But if the trigger condition does not change, although sel changes from 0 to 1, the result of c is still a+b. So, this is not a canonical design thinking.

Therefore, redesign the code according to the designer's idea: when the signal a or the signal b or the signal d or the signal sel changes, execute lines 2 to 5. In this way, it can be ensured that when the signal value of sel is 0, the result of c must be a+b, and when sel is not 0, the result of c must be a+d. Therefore, to add sel to the sensitive list, the code is as follows.

image-20211029113540684

When there are many sensitive signals, it is easy to miss the sensitive signals. To avoid this situation, you can use "*" instead. This "*" refers to all the conditional signals in the "program statement", that is, a, b, d, sel (not including c), and this writing method is also recommended, and the specific code is as follows.

image-20211029113600878

This kind of always statement in which the result of a conditional signal change changes immediately is called "combinatorial logic".

image-20211029113618567

The above code sensitivity list is **"posedge clk", where posedge means rising edge**. That is to say, when clk changes from 0 to 1, the program code is executed once, that is, lines 2 to 5, and the value of c remains unchanged at other times. It should be emphasized that if clk does not change from 0 to 1, then even if a, b, d, sel change, the value of c remains unchanged.

image-20211029113640663

It can be seen that the sensitive list of the above code is "negedge clk", where negedg represents the falling edge . That is to say, when clk changes from 1 to 0, the program code is executed once, that is, lines 2 to 5, and the value of c remains unchanged at other times. It should be emphasized that if clk does not change from 1 to 0, even if a, b, d, sel change, the value of c remains unchanged.

image-20211029113709007

The sensitive list of the above code is "posedge clk or negedge rst_n", that is to say, when clk changes from 0 to 1, or when rst_n changes from 1 to 0, the program code is executed once, that is, lines 2 to 8, others The value of c remains unchanged at time instant. This kind of signal edge trigger, that is, the signal always changes only on the rising or falling edge, is called "sequential logic" , and the signal clk is the clock at this time. Note: To identify whether a signal is a clock is not to look at the name, but to look at where the signal is placed. Only those that are placed in the sensitive list and are edge-triggered are clocks. The signal rst_n is a reset signal, and it is not judged by the name, but placed in the sensitive list and triggered by the same edge. The more important thing is that the "program statement" first judges the value of rst_n, which means that rst_n has the highest priority. are used for reset.

The following points should be paid attention to when designing :

1. * Sensitive variables in the always statement of combinatorial logic must be written in full, or replaced with " " .

2. The assignment of combinational logic devices adopts blocking assignment "=", the assignment statement of sequential logic devices adopts non-blocking assignment "<=" ,

See the section "Blocking and non-blocking assignments" for specific reasons.

5.2 Number system

5.2.1 Digital representation

The most commonly used format for digital representation in Verilog is: <bit width>'<radix><value> , such as 4'b1011. Bit Width: A decimal integer describing the number of bits contained in the constant, which is optional. For example, the 4 in 4'b1011 is the bit width, which is 4 wires in popular understanding. If there is no such item, it can be inferred from the value of the constant. For example 'b1011 infers a bit width of 4, while 'b10010 infers a bit width of 5.

Radix : Indicates how many bases the value is. Can be b, B, d, D, o, O, h or H for binary, decimal, octal and hexadecimal, respectively . Without this entry, the default defaults to a decimal number. For example, 4'b1011 in binary can be written as 4'd11 in decimal, 4'hb in hexadecimal or 4'o13 in octal, or 11 without the base. To sum up, as long as the binary number is the same, it is the same number regardless of whether it is written in decimal, octal or hexadecimal.

Value : A string of ASCII codes representing the real value of a constant determined by the base. If the base is defined as b or B, the value can be 0, 1, x, X, z or Z. If the base is defined as o or O, the values ​​can be 2, 3, 4, 5, 6, 7. If the base is defined as h or H, the values ​​can be 8, 9, a, b, c, d, e, f, A, B, C, D, E, F. For radix d or D, the value sign can be any decimal number: 0 to 9, but not x or z. For example, 4'b12 is wrong, because b means binary, and the value can only be 0, 1, x or z, not including 2. 32'h12 is equal to 32'h00000012, that is, when the value is not written completely, the high bit is filled with 0.

5.2.2 Binary is the basis

In a digital circuit, if chip A transmits data to chip B, such as transmitting 0 or 1 information, chip A and chip B can be connected through a pin, and then chip A controls the output of the pin to be high or low. Level, 0 and 1 are represented by high and low levels. When chip B detects that the pin is at low level, it means it has received 0, and when chip B detects that this pin is at high level, it means it has received 1.

image-20211029114040717

Conversely, if a low level is used to indicate that 1 is received, and a high level is used to indicate that 0 is received, is it possible? Of course, as long as chip A and chip B agree in advance, when chip A wants to send a digital 1, it will set the pin to low level. Chip B detects that the pin is at low level, indicating that the digital 1 is received, and the communication is completed.

image-20211029114120140

A pin has two states of high and low, which can represent two situations of digital 0 and 1 respectively. What if chip A wants to send numbers 0, 1, 2, 3 to chip B?

You can connect chip A and chip B with two pins, that is, two lines: a and b. When both lines are low level, it means sending digital 0; when a is high level and b is low level, it means sending digital 1; when a is low level and b is high level, it means sending digital 2; when When both lines are high, it means sending the number 3.

image-20211029114157781

According to the same principle, when chip A wants to send data 4, 5, 6, 7 to chip B, just add another line. The three wires have a total of 8 states, which can represent 8 numbers. To sum up, different level states of the line can represent different meanings, and as many different states as there are can represent as many numbers.

Let's think about it if chip A wants to send +1, -1, 0, +2 and other numbers to chip B, how to express the positive and negative here? Referring to the previous idea, the meaning of the high and low levels of the line is agreed in advance by the two sides of the chip. In this case, a single line can be used to represent the symbol, for example, low level means positive number, and high level means negative number.

image-20211029114327334

Among the three lines shown in the figure above, line c is used to represent positive and negative, where 0 represents a positive number and 1 represents a negative number. Use line a and line b to represent the value, taking 3'b111 as an example, it can be interpreted as the decimal number 7, it can also be interpreted as the signed number original code "-3", and it can also be interpreted as the signed number complement "-1 , how it is interpreted depends on the engineer's definition of a binary number. As long as this definition does not affect the communication between circuits no problem will occur. Therefore, "0" and "1" in numbers can not only represent literal numerical meanings, but also represent other meanings, such as positive and negative symbols, etc. In the same way, in digital circuits, binary numbers are the foundation of other number systems such as octal, decimal, hexadecimal, signed numbers, unsigned numbers, and decimals. In FPGA design, the most fundamental reason for not knowing the calculation methods of decimal and signed numbers is not knowing the binary values ​​corresponding to these data. As long as you understand the corresponding binary values, many problems can be solved.

Let the students understand this concept better through examples below. Many beginners often ask, how to realize decimal calculation in FPGA? Take "0.5+0.25" as an example. It is well known that the result of 0.5+0.25 is 0.75. Consider how to express 0.5, 0.25 and 0.75 in binary? The specific representation method depends on the engineer's practice, because there are many such representation methods, such as fixed-point decimals, floating-point decimals, and even as discussed above, using a few lines to define by yourself. As long as the communication can be normal, there is no problem. Suppose an engineer uses three wires to define the decimal value represented by the binary value, as shown in the table below.

binary value definition binary value definition
3'b000 0.1 3’b100 0.25
3’b001 0.5 3'b101 0.3
3'b010 0.75 3'b110 0.8
3'b011 0.2 3'b111 0

In order to illustrate that the meaning of binary values ​​can be freely defined, the order of numbers is out of order. Then why only these kinds of decimals? This is because the assumed system only has these kinds of numbers, and if you want to represent more numbers, you can increase the number of lines. After completing the above definition, it is very easy to realize "0.5+0.25", which is actually the "addition" of 3'b001 and 3'b100, expecting to get 3'b010. But directly using 3'b001 + 3'b100 in the table, the result is "101", which is not the desired result, and the code can be written as:

image-20211029114740441

Of course, this is just one way of writing, as long as the corresponding function can be realized and the result is correct, any way of writing is fine.

There may be doubts here, 0.1+0.8 should be 0.9, but there is no representation of 0.9 in the above table. This is actually a defect in the table defined by the designer, or the designer thinks that this situation will not occur. What I want to express here is: As long as the corresponding binary numbers are defined, many functions are easy to design.

Of course, in actual engineering, the agreed and customary practices are usually followed, and there is no need to find another way. For example, the following table is the definition of commonly used fixed-point decimals:

binary value definition binary value definition
3'b000 0.0 3’b100 0.5
3’b001 0.125 3'b101 0.625
3'b010 0.25 3'b110 0.75
3'b011 0.3725 3'b111 0.8725

At this time, if you want to realize 0+0.5=0.5, that is, add 3'b000 and 3'b100, expect to get 3'b100. It can be found that 3'b100 can be obtained by directly using binary 3'b000+3'b100. Similarly, to achieve 0.125+0.75=0.8725, that is, add 3'b001 and 3'b110, expect to get 3'b111. It can be found that 3'b111 can be obtained by directly using binary 3'b001+3'b110.

If the calculation of 0.5+0.75=1.25 is to be realized, it can be seen that 1.25 has exceeded the range of representation at this time, and this problem can be solved by increasing the signal bit width or only representing decimal places. If only decimal places are represented, the result is 0.25, that is, adding 3'b100 and 3'b110, expecting to get 3'b010. It is not difficult to find that 3'b100 + 3'b110 = 4'b1010, expressed in 3 bits is 3'b010, which is 0.25. From the above, it can be seen that the calculation of fixed-point decimals is not complicated, and the calculation can be performed directly after defining the relationship between fixed-point decimals and binary values.

5.2.3 Indeterminate states

As mentioned above, digital circuits only have high level and low level, which represent 1 and 0 respectively. But x and z can often be seen in the code, such as 1'bx, 1'bz. So what are the levels of x and z? The answer is that there is no actual level corresponding to the two. x and z are more of a designer's intent or for simulation purposes, to tell simulators and synthesizers how to interpret this code.

The X state is called an indeterminate state, which is often used to judge the condition, so as to tell the synthesis tool designer that it does not care about its level, whether it is 0 or 1 is fine.

image-20211029114938488

From the above example, it can be seen that the judgment condition is din== 4'b10x0, which is equivalent to din== 4'b1000||din==4'b1010, where "||" is an "or" symbol.

image-20211029115013142

However, it is better to directly write din== 4'b1000||din == 4'b1010 than "din == 4'b10x0" in the design, because this way of writing is more direct and simple.

In the process of simulation, some signals have an indeterminate state, so the designer must carefully analyze whether the indefinite state is reasonable. If you really don't care whether it's 0 or 1, then you can leave it alone. But it is recommended that all signals should not be in an indeterminate state, write clearly whether it is 0 or 1, and do not add "thinking" troubles to the design.

5.2.4 High-impedance state

Z state, generally called a high-impedance state, means that the designer does not drive this signal (neither 0 nor 1), and is usually used in the tri-state gate interface.

image-20211030112458042

The above figure is an application case of the tri-state bus. The connection bus in the figure is both input and output for CPU and FPGA, and it is a bidirectional interface. In general hardware circuits, a pull-up resistor (weak pull-up) or pull-down resistor (weak pull-down) will be connected to this line.

Point A remains high when neither the CPU nor the FPGA is driving the bus. When the FPGA does not drive the bus and the CPU drives the bus, the value of point A is determined by the CPU. When the CPU does not drive the bus and the FPGA drives the bus, the value of point A is determined by the FPGA. But FPGA and CPU can't drive the bus at the same time, otherwise the level of A will be uncertain. Usually, when FPGA and CPU drive the bus, they work according to the agreement negotiated in advance.

image-20211030112556798

The figure above is a typical I2C timing. The bus SDA of I2C is a three-state signal. The I2C protocol has stipulated which time is driven by the master device and which time is driven by the slave device in the above time. Both parties must abide by the agreement and cannot be driven at the same time. So how does the FPGA achieve the behavior of "not driving" in the design? This is because there are three-state gates inside the FPGA.

image-20211030112615735

The tri-state gate is a piece of hardware, and the figure above is its typical structure. The tri-state gate has four interfaces, such as the write enable wr_en, write data wr_data, read data rd_data and the tri-state signal data connected to external devices as shown in the figure above.

It should be noted that the write enable signal, when the signal is valid, the tri-state gate will assign the value of wr_data to the tri-state line data, at this time the value of data is determined by wr_data, when wr_data is 0, the value of data is 0; when wr_data When it is 1, the value of data is 1. When the write enable signal is invalid, no matter what the wr_data value is, it will not affect the external data value, that is, it will not be driven.

In Verilog, the above functions are realized by the following code:

image-20211030112644309

When the synthesizer sees these two lines of code, it knows that it will be synthesized into a three-state gate, and this is the role of the high-impedance z. In addition, it can be noticed that the use of tri-state lines on the hardware is to reduce pins, and there is no need to reduce wiring in the FPGA, so it is meaningless to use tri-state signals. Therefore, it is recommended that you do not use the high-impedance state "z" inside the FPGA when designing, because there is no need to add "thinking" troubles to yourself. Of course, if the high-impedance state is used in the design, no error will be reported, and the function can also be realized.

Generally speaking, the high-impedance state "z" means the behavior of "not driving the bus". In fact, digital circuits are high or low, and there are no other levels.

5.3 Arithmetic operators

image-20211030112732563

image-20211030145125938

Arithmetic operators include addition "+", subtraction "-", multiplication "*", division "/" and remainder "%". The commonly used arithmetic operators mainly include: addition "+", subtraction "-" and multiplication "*".

Note that the commonly used operations do not include division and remainder operators. This is because division and remainder operators are not built with simple gate logic, and the corresponding hardware circuits are relatively large. Addition and subtraction are the simplest operations, and multiplication can be disassembled into multiple addition operations, so the circuits corresponding to addition, subtraction and multiplication are relatively small. The division is different. Students can recall the steps of the division, which involves multiple multiplications, shifts, addition and subtraction, so the circuit corresponding to the division is complicated, which also requires the designer to be careful when designing Verilog. Use division with caution.

5.3.1 Addition operator

First learn the addition operator, the symbol "+" can be used directly in Verilog code:

image-20211030112934610

Its circuit diagram is as follows:

image-20211030112947325

A synthesizer can recognize the addition operator and turn it into a circuit like the one shown above. The binary addition operation is similar to the decimal addition operation, the decimal system is every ten, and the binary is every two. The basic operation of binary addition is as follows:

image-20211030113004606

5.3.2 Subtraction operator

Subtraction operator, the symbol "-" can be used directly in Verilog code:

image-20211030113035316

Its circuit diagram is as follows:

image-20211030113049315

A synthesizer can recognize the subtraction operator and turn it directly into the circuit shown above.

The binary subtraction operation is similar to the decimal subtraction operation, and there is also the concept of borrowing. In decimal system, one is borrowed as ten, and in binary, one is borrowed as two. The basic operation of 1-bit subtraction is as follows:

image-20211030113108106

5.3.3 Multiplication operator

Multiplication operator, the symbol "*" can be used directly in Verilog code:

image-20211030113141837

Its circuit diagram is as follows:

image-20211030113153342

The synthesizer can recognize the multiplication operator and turn it directly into the circuit shown above. The binary multiplication operation is similar to the decimal multiplication operation, and the calculation process is the same. The basic operation of 1-bit multiplication is as follows:

image-20211030113213925

The multiplication between multiple digits is the same as the decimal calculation process. For example, the calculation process of 2'b11 * 3'b101 is as follows:

image-20211030113232235

5.3.4 Division and remainder operators

The division operator can use the symbol "/" directly in Verilog code, while the remainder operator is "%":

image-20211030142720756

The schematic diagram of the division circuit is as follows:

image-20211030142733602

The schematic diagram of the remainder circuit is as follows:

image-20211030142749890

The synthesizer can recognize the division operator and the remainder operator, but these two operators include a large number of multiplication, addition and subtraction operations, so the circuit of the divider in the FPGA is very large, and the synthesizer may not be directly converted into circuit shown in Fig.

There may be doubts here: Why is division and remainder taking a lot of resources? Let's analyze the process of decimal division and remainder, taking 122 divided by 11 as an example.

image-20211030142812957

In the process of doing the above operations, multiple shifts, multiplications, subtractions and other operations are involved. That is to say, multiple multipliers and subtractors are used to perform a division operation, which requires relatively large hardware resources. The same is true for binary operations.

Therefore, in design code, division and remainder are generally not used. There are various ways to avoid division and remainder operations in the algorithm. Therefore, in digital signal processing, communication, and image processing, you will find a lot of multiplication, addition and subtraction, etc., but rarely see division and remainder operations. But in the simulation test, division and remainder can be used, because it is only used for simulation test and does not need to be synthesized into a circuit, so naturally there is no need to care about how many resources are occupied.

5.3.5 Experience Summary

bit width problem

When writing code, you need to pay attention to the bit width of the signal. The final result depends on the bit width of the signal to the left of the "=" sign, save the low bit and discard the high bit. For example:

image-20211030142922330

The bit width of the signal c is 1 bit, so the result of the operation finally reserves the lowest 1 bit, so the value of c is 1'b0. Since the bit width of d is 2 bits, the lower 2 bits of the operation result can be reserved, so the value of d is 2'b10. Since the bit width of e is 3 bits, the lower 3 bits of the operation result can be reserved, so the value of e is 3'b010. "1" is 32 bits by default, and the result of 1+1 is also 32 bits, but since the bit width of f is only 3 bits, the lower 3 bits of the operation result can be reserved, so the value of f is 3'b010.

The same is true for subtraction operations. Take the following code as an example:

image-20211030142952611

The binary value obtained from "0-1" is "1111111111….", but the saved result depends on the bit width of the signal to the left of the "=" sign. The bit width of c is 1, and the lowest 1 bit is reserved, so the value of c is 1'b1. Since the bit width of d is 2 bits, the lower 2 bits are reserved in the result, so the value of d is 2'b11. Since the bit width of e is 3 bits, the lower 3 bits are reserved in the result, so the value of e is 3'b111. The bit width of f is 4 bits, so the lower 4 bits of the operation result can be reserved, so the value of f is 4'b1111.

When writing the multiplication code, you also need to pay attention to the bit width of the signal. The final result depends on the bit width of the signal to the left of the "*" sign. Save the low bit and discard the high bit:

image-20211030143016965

The binary value obtained by "2'b11 * 3'b101" is "4'b1111", but the saved result depends on the bit width of the signal to the left of the "*". The bit width of c is 1, and the lowest 1 bit is reserved, so the value of c is 1'b1. Since the bit width of d is 2 bits, the lower 2 bits are reserved in the result, so the value of d is 2'b11. Since the bit width of e is 3 bits, the lower 3 bits are reserved in the result, so the value of e is 3'b111. The bit width of f is 4 bits, so the lower 4 bits of the operation result can be reserved, so the value of f is 4'b1111. It should be noted that h, the signal has 5 bits, 4'b1111 is assigned to the 5-bit signal, and the result is that the high bits are filled with 0, so the result is 5'b01111.

The origin of complement

When FPGA implements various algorithms, the most important thing is to ensure the correctness of the calculation results, otherwise everything is meaningless. When analyzing the addition and subtraction operators, it can be found that whether the signal bit width for saving the result is reasonable has a great influence on the correctness.

For example the following addition operation:

image-20211030143139276

It can be seen from the above table that if the carry is not preserved, the calculation result is incorrect when the addition occurs, and the calculation result is correct only if the carry is preserved. From this, we can draw a conclusion: when using addition, in order to ensure the correctness of the result, the carry must be saved, that is, the result must expand the bit width.

For example, when adding two 8-bit numbers, the result needs to be extended by one bit, and the bit width is set to 9 bits.

image-20211030143209482

Next, let's analyze the subtraction operation, as shown in the following table:

image-20211030143329162

Note that in the table and 2'b00-2'b01, the result is 2'b11, the corresponding decimal value is 3, but the expected result is "-1". In the same way, 2'b01 - 2'b11, the result is 2'b10, the corresponding decimal value is 2, and the expected result is "-2", so the above result is incorrect.

When the expected result is positive or negative, a sign bit can be added to distinguish the positive or negative result. The representation method agreed in the industry is that when the highest bit is 0, it means a positive number, and when the highest bit is 1, it means a negative number. The value after the sign bit is represented by the lower 2 bits, and the result is as follows:

image-20211030143414060

It can be seen from the above table that after adding the sign bit, there will still be some problems that the operation results do not meet the expectations. For example, 2'b00-2'b01 in the table, the result is 3'b111, the corresponding decimal value is -3, but the expected result is "-1". So the above result is still incorrect.

Now, re-convert the binary number "000~111" as follows :

a. Positive number: remain unchanged

b. Negative number: the sign bit remains unchanged, and the value is inverted and 1 is added .

That is to say, if it is a positive number "+1", it was represented by "001" before, but it is still represented by "001" now. If it is a negative number "-1", it was represented by "101" before, but now it is represented by "111". The negative number "-3" was previously represented by "111", but now it is represented by "101". This representation is the complement representation .

After expressing it in complement code, let’s analyze the result:

image-20211030145353945

It can be seen that the results in the above table are all correct and consistent with expectations. This process did not make any changes to the code at all, but the correct result was achieved by changing the definition of the data.

In the previous discussion, the operations of addend, summand, subtrahend, and minuend did not use signed numbers. Now re-represent it using the two's complement of the signed number. Assuming that the addend, the summand, the subtrahend and the minuend are all 2 bits (the range is -2~1), considering the reason of carry and borrow, the result is represented by 3 bits (the range is -4~3). Because the bit width of the result becomes 3 bits, both the subtrahend and the minuend are expanded to be represented by 3 bits, as shown in the following table:

image-20211030145455123

image-20211030145507551

The summary operation steps are as follows:

  1. According to "human common sense", the maximum and minimum values ​​of the result are expected to determine the signal bit width of the result .
  2. Extend the bit width of addend, subtrahend and other data to make the bit width of the result consistent.
  3. Perform calculations in binary addition and subtraction.

Through the above method, what is obtained is the result of complement code. In fact, in FPGAs and even computer systems, all data is stored in complement form . If you want to know more about complement code, you can refer to related materials.

5.4 Logical operators

image-20211030145547059

image-20211030145556624

There are 3 logical operators in Verilog HDL language, they are:

(1) &&: logic and ;

(2) | | : logical or ;

(3) !: Logical NOT .

5.4.1 Logical AND

"&&" is a binary operator, which requires two operands, such as a && b.

(1) 1-bit logical AND

image-20211105212729170

When both A and B are 1, C is 1, otherwise C is 0.

The corresponding hardware circuit diagram is as follows:

image-20211105212800939

(2) Multi-bit logical AND

image-20211105212816535

C is 1 when neither A nor B is 0, otherwise it is 0.

image-20211105212839123

5.4.2 Logical OR

"||" is a binary operator, which requires two operands , such as a||b.
(1) 1-bit logical OR

image-20211105213147969

One of A and B is 1, C is 1, otherwise C is 0.

The corresponding hardware circuit diagram is shown in the figure below:

image-20211105213306687

(2) Multi-bit logical OR

image-20211105213322593

If one of A and B is non-zero, C is 1, otherwise C is 0.

The corresponding hardware circuit diagram is shown in the figure below:

image-20211105213347308

5.4.3 Logical NOT

"!" is a unary operator, which requires only one operand, such as! (a>b).

image-20211105213442864

For the operand a, it is necessary to judge whether not a is true. If it is true, execute the operation inside {}, and if it is false, end the operation.
The following table is the truth table of logic operations, which indicates the values ​​obtained by various logic operations when the values ​​of a and b are in different combinations.

image-20211105213506720

The final result of logical operators is only logically true or logically false, that is, 1 or 0. In general, when logical operators are used as judgment conditions, the logical AND operation can only be two 1-bit-wide numbers. Only when two expressions are true at the same time can it be true, and if one of them is false, it can be false.

If the operand is multi-bit, the operand can be regarded as a whole. If each bit in the operand is 0, it is a logic 0 value;
if there is a 1 in the operand, it is a logic 1 value.

image-20211105213540379

Since neither 4'b0111 nor 4'b1000 is 0, it is considered logically true if it is not 0, so the above code is equivalent to the following code.

image-20211105213557611

That is, the result is that a is logically true, b is logically true, and c is logically false.

5.4.4 Experience Summary

(1) Priority of logical operators

Among the logical operators - "&&" and - "||" have lower priority than arithmetic operators; "!" has higher priority than binocular logical operators

Examples are as follows:

image-20211105213736040

(2) Both sides of the logical operator correspond to 1-bit signals

Experience :Logical operators on both sides correspond to 1-bit signals

image-20211105213859092

Pay attention to the above code, where a and b are both multi-bit signals, indicating that two multi-bit signals are logically ANDed. The correct understanding of this code is: when a is not equal to 0 and b is not equal to 0, the value of d is 1. However, even engineers with many years of work experience can hardly intuitively understand the meaning implied by the above code. The concept that not equal to 0 means logically true and equal to 0 means logically false is easily overlooked.

Therefore, although there is no error in the above code, the designer should not write the code for the purpose of showing off the technology in the design, and this way of writing is prone to misdesign, for example, it may originally express assign d = a & b, but in the end, the above code was written because the design was not intuitive enough, which led to design problems. Therefore, it is very important to write intuitively and understandably in the design, so that you and others can immediately understand the meaning of the code when they see the code, so it is recommended to write the above code in the following form.

image-20211105213942868

(3) Multi-use parentheses to distinguish priorities

Experience 2 :Don't try to remember precedence, use parentheses more

In fact, engineers do not remember all the prioritization in their work, and remembering all the priorities will not greatly improve the work efficiency of engineers. In the design, you may encounter the following code:

(1) a < b && c > d ;

(2) a = = b | | c = = d ;

(3)! a | | a > b 。

If you don't remember the precedence of operators, when you encounter situations like these three examples, you will definitely spend a certain amount of time to sort out your thoughts and think about which part to judge first. If the engineer can remember the priority, it also needs to communicate and check the work of these codes, and human beings are prone to make mistakes, and these mistakes are often overlooked and difficult to be checked.

Therefore, in order to improve the readability of the program and clearly express the priority relationship between operators, it is recommended to use more brackets in the design. The three examples above can be written as:

1)( a < b) &&( c > d);

2)( a = = b) | |( c = = d);

3)(!a) | |( a > b)。

(4) Use less logical NOT

Experience 3 :Use "logic and" and "logic or" more, and use less logic not

"Logical and" translated into Chinese is "and", and "logical or" translated into Chinese is "or". Suppose there is a signal flag, 0 means idle, 1 means busy. "(!(flag== 0) && a== 4' b1000)", which reads as "take the opposite state when idle, and the condition is true when a is equal to 4' b1000". This is very difficult to read, and when reading this code, you need to turn your head a little bit more and think about it a little bit more. In order to make the code more intuitive, it is suggested that the above example be written as "flag== 1 && a==4' b1000", which reads as "when busy and a is equal to 4' b1000" the condition is true.

5.5 Bitwise Logical Operators

image-20211105215609897

image-20211105215735525

Note: ~ ^, ^ ~ (binary XOR, NOR): (equivalent to NOR operation).

There are the following bitwise operators in Verilog HDL language:

~ (unary NOT): (equivalent to NOT operation)

& (binary AND): (equivalent to AND operation)

| (binary or): (equivalent to OR operation)

^ (binary XOR): (equivalent to XOR operation)

These operators operate bitwise on corresponding bits of the input operands and produce a vector result. Each truth table in the figure below shows the result of bitwise operation for different bitwise logical operators:

image-20211106195003983

5.5.1 Monocular bitwise AND

The unary bitwise AND operator &, after the operator is the signal that needs to be logically operated, which means the AND operation between each bit of the signal. For example

Reg[3:0] A,C;

assign C=&A;

The above code is equivalent to C = A[3] & A[2] & A[1] & A[0]; if A=4'b0110, the result of C is 0.

5.5.2 Monocular bitwise OR

Monocular bitwise OR operator |, after the operator is the signal that needs to be logically operated, indicating that the signal is ORed between bits. For example

reg[3:0] A,C;

assign C=|A;

The above code is equivalent to C = A[3] | A[2] | A[1] | A[0]; if A=4'b0110, the result of C is 1.

5.5.4 Binocular bitwise AND

Binocular bitwise AND operator &, the signal is located on the left and right sides of the operator, which means that the corresponding phase AND operation is performed on the two signals. For example

reg[3:0] A,B,C;

assign C = A & B;

The above code is equivalent to: C[0] = A[0] & B[0], C[1] = A[1] & B[1], C[2] = A[2] & B[2 ], C[3] = A[3] & B[3]. If A=4'b0110, B=4'b1010, the result of C is 4'b0010.

If the operand lengths are not equal, the operand with the smaller length is padded with 0 on the leftmost side. For example,

reg[1:0] A;

reg[2:0] B;

reg[3:0] C;

assign C = A & B;

The above code is equivalent to: C[0] = A[0] & B[0], C[1] = A[1] & B[1], C[2] = 0& B[2], C[ 3] = 0 &0.

5.5.5 Binocular bitwise OR

Binocular bitwise OR operator |, the signal is located on the left and right sides of the operator, indicating that the corresponding phase OR operation is performed on the two signals. For example

reg[3:0] A, B, C;

assign C = A | B;

The above code is equivalent to: C[0] = A[0] | B[0], C[1] = A[1] | B[1], C[2] = A[2] | B[2 ], C[3] = A[3] | B[3]. If A=4'b0110, B=4'b1010, the result of C is 4'b1110.

If the operand lengths are not equal, the operand with the smaller length is padded with 0 on the leftmost side. For example,

reg[1:0] A;

reg[2:0] B;

reg[3:0] C;

assign C = A | B;

The above code is equivalent to: C[0] = A[0] | B[0], C[1] = A[1] | B[1], C[2] = 0 | B[2], C [3] = 0|0.

5.5.6 Binocular bitwise XOR

The binocular bitwise XOR operator ^, the signals are located on the left and right sides of the operator, which means to perform the corresponding phase XOR operation on the two signals. XOR refers to 0 0=0,1 1=0,0^1=1, that is, the same is 0, and the difference is 1. For example

reg[3:0] A, B, C;

assign C = A ^ B;

The above code is equivalent to: C[0] = A[0] ^ B[0], C[1] = A[1] ^ B[1], C[2] = A[2] ^ B[2 ], C[3] = A[3]
^ B[3]. If A=4'b0110, B=4'b1010, the result of C is 4'b1100.

If the operand lengths are not equal, the operand with the smaller length is padded with 0 on the leftmost side. For example,

reg[1:0] A;

reg[2:0] B;

reg[3:0] C;

assign C = A | B;

The above code is equivalent to: C[0] = A[0] ^ B[0], C[1] = A[1] ^ B[1], C[2] = 0 ^ B[2], C [3] = 0^0.

5.5.7 Experience Summary

Difference Between Logical Operators and Bitwise Operators

Logical operators include &&, ||, !, and bitwise operators include &, |, ~ . So what is the difference between logical operators and bitwise operators? Comparing the logical AND "&&" and the bitwise AND "&", it can be seen that the operation of the logical AND operatorThere are only two outcomes: logically true or logically false, that is, 1 or 0; and "&" is a bitwise operator,for two multi-bit wide data operations. For bitwise operators, two numbers are ANDed, ORed, or NOTed bitwise.

image-20211107101136767

The result of the above operation is: a=1'b1, b=1'b1, c=1'b0, d=4'b0000, e=4'b1111, f=4'b1000.

5.6 Relational Operators

image-20211107101351496

image-20211107101503199

The relational operators are: > (greater than), < (less than), >= (not less than), <= (not greater than), == (logically equal), and ! = (logical not equal).

Relational operators evaluate to true (1) or false (0). If one of the operands is x or z, the result is x . Example: 23 > 45 : The result is false ( 0 ). 52 < 8'hxFF: The result is x.

If the operands are of different lengths, the shorter operand is zero-padded in the most significant bit direction (left) . For example: 'b1000 >= 'b01110 is equivalent to: 'b01000 >= 'b01110, which is false (0).

In the comparison of logical equality and inequality, as long as one operand contains x or z, the comparison result is unknown (x). For example, if Data = 'b11x0; Addr = 'b11x0; then Data == Addr, the comparison result is uncertain, that is The result is x.

5.7 Shift operators

There are two shift operators in Verilog HDL, namely "<<" (left shift operator) and ">>" (right shift operator).

The following describes the usage of the two respectively:

image-20211107102901190

image-20211107102911492

5.7.1 Left shift operator

In Verilog HDL, "-"<< represents the left shift operator. Its general expression is:

A << n;

Among them, A represents the operand to be shifted, and n represents the number of bits to be left shifted. The meaning of this expression is to shift the operand A to the left by n bits. The left shift operation is a logical shift, and 0 needs to be used to fill the vacancy shifted out, that is, 0 is filled in the lower bits. To shift left by n bits, it is necessary to fill n 0s.

image-20211107103016873

Since the above code is left-shifted by 2 bits, 2 zeros are filled in the lower bits, so the running result of the above code is: a = 4'b1100.
There are three points worth noting in the left shift operation:
(1) The left shift operation does not consume logic resources, even the AND gate and the NOT gate are not needed, it is just the connection of lines .

image-20211107103040064

The above code shifts the signal b to the left by two bits and assigns it to c. The corresponding hardware circuit is as follows:

image-20211107103103978

(2) The left shift operation needs to store the result according to the bit width

You may have seen the following codes during the learning process: 4'b1001<<1=4'b0010 and 4'b1001<<1=5'b10010

Why is the operand also 4'b1001, which is shifted left by one bit, but the result is 4'b0010 and 5'b10010? This is because after the left shift operation, it depends on how many bits are used to store the result.

image-20211107103702482

In the above code, since a is 4 bits, only 4 bits can be saved, so b is shifted left by 1 bit and assigned to 4 bit a, and the result after filling the shifted bits with 0 is a = 4'b0010;

image-20211107103721433

In the above code, since a is 5 bits, it can store 5 bits of results, so b is shifted left by 1 bit and assigned to 5 bit a, and the result is a = 5'b10010 after filling the shifted bits with 0; ( 3**)
left The operand of the shift operation can be a constant or a signal**. Similarly, the shift number, constant for the left shift operation can also be a signal.

image-20211107103747278

In the above code, cnt is incremented by 1 every clock, and since it is 3 bits, the value is 0~2. a is 4'b1 shifted left by cnt bits. When cnt is equal to 0, shift left by 0 bit, a is equal to 4'b1; when cnt is equal to 1, shift left by 1 bit, a is equal to 4'b10. By analogy, each clock change of a is as follows:

image-20211107103805477

It should be noted that when the shift number is a signal, the integrated circuit is not a simple connection, and the selector shown in the figure below may be integrated . However, even so, the resources consumed by this hardware circuit are still relatively small.

image-20211107103822414

5.7.2 Right shift operator

In Verilog HDL, use ">>" to represent the right shift operator. Its general expression is:

A >>n;

Among them, A represents the operand to be shifted, and n represents the number of bits to be shifted right. The meaning of this code is to shift the operand A to the right by n bits.

There are three points worth noting in the right shift operation:
(1) The right shift operation is a logical shift, and 0 is needed to fill the vacancy shifted out, that is, to fill the high bits with 0, and how many 0s to fill
depends on width of the signal that saves the result .

image-20211107103914795

4'b0111 The result of shifting right by two bits is 2'b01. Since a has 6 bits, assigning 2 bits to 6 bits needs to add 0 to the high bit, so 4 0s need to be added. So the result of running the above code is:

a = 6'b0001
(2) Similar to the left shift operation, the right shift operation does not consume logic resources, even the AND gate and the NOT gate are not needed, it is just the
connection of lines .

image-20211107103936593

The above code shifts the signal b to the left by two bits and assigns it to a. The corresponding hardware circuit is shown in the figure below.

image-20211107103951964

(3) The operand of the left shift operation can be a constant or a signal. Likewise, the shift number for a right shift operation can be either a constant or a signal .

image-20211107104010745

In the above code, cnt is incremented by 1 every clock, and since it is 3 bits, the value is 0~2. a is 4'b1000 shifted right by cnt bits. When cnt is equal to 0, shift right by 0 bit, a is equal to 4'b1000; when cnt is equal to 1, shift right by 1 bit, a is equal to 4'b0100. By analogy, the change of each clock of a is shown in the figure below.

image-20211107104037650

Similar to the left shift operation, in the right shift operation, if the shift number is a signal, the integrated circuit is not a simple connection, but may synthesize a selector as shown in the figure below. However, also in this case, the resources consumed by such hardware circuits are still relatively small.

image-20211107104055622

5.7.3 Experience Summary

Multiply by left shift

In FPGA, multiplication operation should be avoided as much as possible, because this kind of calculation needs to occupy large hardware resources, and the operation speed is relatively slow. When you have to use multiplication, try to multiply by 2 to the Nth power , so that the multiplication operation can be realized by using the left shift operation in the design, thereby greatly reducing hardware resources.

When the multiplier is a constant of 2 to the Nth power, the multiplication can be realized by shift operation. For example: a 2 is equivalent to a<<1; a 4 is equivalent to a<<2; a*8 is equivalent to a<<3, and so on. Even if the multiplier is not a constant of 2 to the Nth power, the implementation can be simplified by a shift operation. For example:

image-20211107104134562

Both b and c in the above code can realize a*127, but the first line consumes one multiplication, while the second line only uses one subtractor
.

image-20211107104147477

In the above code, both b and c can realize a*67, but the first line consumes one multiplication, while the second line only uses two adders, thus saving resources.

It can be noticed that the multipliers in the above two examples are all constants, so does this kind of multiplication also take time and effort to consider optimization during design? In fact, it is unnecessary, because the comprehensive tools are very powerful now. When the tool finds that the multiplier is a constant, it will automatically optimize according to the above process. That is to say, multiplying by a constant does not consume multiplier resources in essence, so you can use it with confidence.

But when the multiplier is not a constant, it is necessary to pay attention to the use of multiplication. Try to convert the signal into a form related to 2 to the Nth power. For example, when the data needs to be expanded and calculated later, do not expand the data by 100 times according to the usual thinking, but directly expand it by 128 times (It is not possible to expand decimals by 100 or 1000 times according to conventional thinking, and to amplify according to the nth power of 2, which reduces the occupancy of resource space)。

Use right shift to implement division operation

In FPGA design, it is necessary to avoid division as much as possible, and it is even strictly forbidden to use "/" for division calculation. This is because the divider will occupy a huge amount of resources, which is more than that of the multiplier, and in many cases the result cannot be obtained within one clock cycle. And when you have to use division, you should try to convert the division into the form of dividing by 2 to the Nth power, so that you canUse the right shift operationTo realize the division operation, thereby greatly reducing hardware resources .

When the divisor is a constant of 2 to the Nth power, division can be realized by shift operation. For example: a/2 is equivalent to a>>1; a/4 is equivalent to a>>2; a/8 is equivalent to a>>3, and so on.

Different from the left shift, when the divisor is not a constant of 2 to the Nth power, the implementation cannot be simplified simply by a shift operation. In summary, division should be avoided as much as possible in FPGA design.

One-hot encoding using left shift

One-hot code, also called one-hot code, is a code system in which only one bit is 1 and the others are all 0 . For example 8'b00010000, 8'b1000000 etc.

The one-hot code is very useful in design, it can be used to represent the state of the state machine to make the state machine more robust, and it can also be used in a multi-choice circuit to represent choosing one of them.

Using the left shift operation, one-hot codes can be easily generated, for example, 4'b0010 can be generated, which can be 4'b1 << 1. Similarly, a code system in which one bit is 0 and the others are 1 can also be generated. For example to generate 4'b1011, it can be ~(4'b1 <<2). Other desired numeric results can also be produced using left shifts:

For example, to generate 5'b00111, it could be (5'b1<<3)-1.

For example, to produce 5'b11100, could be ~((5'b1<<2)-1).

5.8 Conditional Operators

image-20211107192312809

image-20211107192334644

5.8.1 Ternary operator

**Conditional operator (?: )** in Verilog HDL syntax has three operands (that is, ternary operator), and its format is generally expressed as:

image-20211107192358180

Its meaning is: when the "conditional expression" is true (i.e. logic 1), execute the "true expression"; when the "conditional expression" is false (i.e.
logic 0), execute the "false expression". That is, when condition_expr is true (that is, the value is 1), select true_expr; if condition_expr
is false (the value is 0), select false_expr. If condition_expr is x or z, the result will be the value of the bitwise operation of true_expr and false_expr according to the following logic: 0 and 0 yield 0, 1 and 1 yield 1, and x otherwise.

Application examples are as follows:

image-20211107192422886

In the above expression, if s is true, assign t to r; if s is false, assign u to r.

The corresponding hardware circuit diagram is shown below.

image-20211107192454340

The use of conditional operators has the following points to note :

(1) The function of the conditional expression is actually similar to a multiplexer , as shown in Figure 1.3-8. Also, it can be replaced with an if-else statement.

image-20211107192527485

(2) Conditional operators can be used for conditional assignment in data flow modeling, and in this case conditional expressions act as control switches . For example:

image-20211107192550223

Among them, if the expression Marks > 18 is true, then Grade_A is assigned the value of student; if Marks > 18 is false, then
Grade_C is assigned the value of student.

The corresponding hardware circuit diagram is shown below.

image-20211107192607991

(3) Conditional operators can also be nested, and each "true expression" and "false expression" can itself be a conditional
expression . For example:

image-20211107192719206

The meaning of the above code is: if the expression M == 1 is true, then judge whether CTL is true, if CTL is true, assign A to OUT, if it is false, assign B to OUT; if M = = 1 is false, then judge whether CLT is true, if CLT is true, assign C to OUT, if false, assign D to OUT.

The corresponding hardware circuit diagram is as follows:

image-20211107192740274

5.8.2 if statement

The syntax of the "if" statement is as follows:
if(condition_1)

procedural_statement_1;
{else if(condition_2)

procedural_statement_2};
{else

procedural_statement_3};

Its meaning is: if condition_1 is satisfied, regardless of whether the rest of the conditions are satisfied, procedural_statement_1 will be executed, and neither procedural_statement_2 nor procedural_statement_3 will be executed.

If condition_1 is not satisfied and condition_2 is satisfied, then procedural_statement_2 is executed, and neither procedural_statement_1 nor procedural_statement_3 is executed.

If condition_1 is not satisfied and condition_2 is not satisfied, procedural_statement_3 is executed, and neither procedural_statement_1 nor procedural_statement_2 is executed.

Let's illustrate with an example:

if(Sum < 60) begin

Grade = C;

Total_C = Total _C + 1;
end
else if(Sum < 75) begin

Grade = B;

Total_B = Total_B + 1;
end
else begin

Grade = A;

Total_A = Total_A + 1;
end

Note that conditional expressions must always be enclosed in parentheses, and may be ambiguous if the if - if - else format is used,
as shown in the following example:
if(Clk)
if(Reset)

Q = 0;
else

Q = D;

There is a question here: Which if statement does the last else belong to? Does it belong to the condition of the first if (Clk) or the condition of the second if (Reset)? This is done in Verilog HDL by combining the else with the nearest none Else's if statement is associated to
resolve. In this example, the else is associated with the inner if statement.

Here's another example of an if statement:
if(Sum < 100)

Sum = Sum + 10;
if(Nickel_In)

Deposit = 5;
Elseif (Dime_In)

Deposit = 10;
else if(Quarter_In)

Deposit = 25;
else

Deposit = ERROR;
suggestion

1. Conditional expressions need to be enclosed in parentheses .

2. If it is an if - if statement, please use the block statement begin — end , as shown below.
if (Clk) begin

if(Reset)

Q = 0;

else

Q = D;
end

The above two suggestions are to make the code clearer and prevent errors.

5.8.3 case statement

The case statement is a multi-way conditional branch form , and its syntax is as follows:
case(case_expr)
case_item_expr{case_item_expr} :procedural_statement
. . . . . .
[default:procedural_statement]
endcase

Under the case statement, the conditional expression case_expr is first evaluated, and then each branch item is evaluated and compared in turn, and the statement in the first branch that matches the value of the conditional expression is executed . Multiple branch items can be defined in 1 branch, and these values ​​do not need to be mutually exclusive. The default branch overrides all other branches not covered by a branch expression.

image-20211107193238224

Writing suggestions: The default item of the case statement must be written to prevent the generation of latches.

5.8.4 Select statement

There is a commonly used selection statement in Verilog syntax, and its syntax is:

vect[a +: b]或 vect [a -: b]

vect is the variable name, a is the starting position,Plus or minus signs represent ascending or descending order, b represents the width for ascending or descending order .

vect[a +: b] is equivalent to vect[a : a+b-1] , the interval of vect starts from a and counts b times in the direction greater than a; vect[a -: b] is equivalent to vect[a : a -b+1] , the interval of vect is counted b times from a to the direction where a is smaller. a can be a constant or a variable number, but b must be a constant.

Example 1 : vect[7 +: 3]; where, the starting position is 7, + represents ascending order, and the width is 3. That is, count 3 numbers from 7 to the direction greater than 7. Its equivalent form is: vect[7 +: 3]== vect[7 : 9].

Example 2 : vect[9 -: 4]; among them, the starting position is 9, - represents the descending order, and the width is 4. That is, count 4 numbers from 9 to the direction smaller than 9. Its equivalent form is: vect[9 -: 4]== vect[9 : 6].

The most common form of this syntax in actual use is to use a as a variable number. For example, you need to design
code with the following functions:

When cnt==0, assign din[7:0] to data[15:8]; when cnt==1, assign din[7:0] to data[7:0].

When designing, it can be written as: data[15-8 cnt -: 8] <= din[7:0] (At this time, 15-8 cnt needs to be
regarded as a whole, which will happen with the change of cnt changes), so that the streamlining of the code is completed.

The hardware circuit structure of the selection statement is shown in the figure below, which is essentially a selector. When cnt==0, select the latch of data[15:8], assign din[7:0] to data[15:8], and the latch of data[7:0] keeps the output unchanged ; When cnt==1, select the latch of data[7:0], assign din[7:0] to data[7:0], and the latch of data[15:8] keeps the output Change.

image-20211107193442661

Experience conclusion: In actual projects, the form of selection statement vect[a +: b] or vect [a -: b] can be used for code writing, which will help to simplify the design code.

5.8.5 Experience Summary

The if statement and the case statement are two very important statements in Verilog. If and case statements have certain correlations and
differences. The same thing is that the two can achieve almost the same function. The following mainly introduces the differences between the two.
There is a priority between each branch of the if statement, and the synthesized circuit is similar to a cascade structure. Each branch of the case statement is equal, and the synthesized circuit is a multiplexer . Therefore, the delay of the logic circuit obtained by combining multiple if else-if statements may be slightly larger than that of the case statement . For beginners, they often like to use the if else-if statement in the process of learning Veriolg at the beginning, because this syntax is more straightforward to express. But in projects where the running speed is more critical, the effect of using the case statement will be better. Let's compare it through a specific case, using if statement and case statement to describe the comprehensive result of the same functional circuit.

First is the code written with the if statement:

image-20211107193601401

image-20211107193618227

Its synthesized RTL view is shown below.

image-20211107193643645

As can be seen from the RTL diagram shown in the above figure, this circuit contains two two-to-one multiplexers, and the priority of the right side is higher than that of the left (because the value of q is directly related to the two-to-one selection on the right device connection), when en[0] is not 1, it will continue to judge en[1]. That is, inThe circuit synthesized under the if statement has priority

Next, analyze the code described using the case statement.

image-20211107193718179

Its synthesized RTL view is as follows:

image-20211107193742651

As can be seen,The circuit synthesized by the logic code written by the case statement is parallel, has no priority, and does not affect the running speed of the circuit

Although in the RTL view, there is a big difference between the circuits synthesized by the two statements, but because the current development tools are smart enough, the circuit will be automatically optimized during layout and routing, and it will eventually be mapped to the circuit inside the FPGA. There is basically no difference between them .

image-20211107193823156

image-20211107193833956

Finally, summarize the difference and connection between if statement and case statement:

The If statement has priority, and the part after the else is executed only when the condition under the if is not met. The case statement is parallel and has no priority, which can be clearly observed in the RTL view synthesized by the two. However, since the current simulation and synthesis tools are powerful enough, the final synthesis results of if...else... and case... statements are actually not different, they are just two different implementation methods, so there is basically no need to consider the difference between the two difference. Under the premise of not affecting the function, the designer does not need to do local optimization work, for example, there is no need to consider the difference in resource consumption of if/case statements, and no need to consider optimizing circuits. Only under the premise of affecting the function (that is, reporting an error due to sequence constraints), optimize the circuit according to the prompts.

5.9 Concatenation Operators

image-20211107193940242

image-20211107193949282

The concatenation operation is an operation that combines small expressions to form a large expression, and its form is as follows:

{expr1, expr2, . . ., exprN} ;

The splicing character does not consume any hardware resources, it just changes the combination of lines , you can refer to the following examples:

image-20211107194014357

Concatenation of non-fixed-length constants is not allowed because the length of non-fixed-length constants is unknown. Therefore, the code shown below is not grammatical. {Dbus,5}; //concatenation operations on non-fixed-length constants are not allowed

Guess you like

Origin blog.csdn.net/Royalic/article/details/121196365