Hi, Say I have a register described by a clocked process: signal test_int : integer; signal event1 : std_logic; signal event2 : std_logic; signal event3 : std_logic; process(clk) begin if(clk'event and clk='1') then if(event1 = '1') then test_int <= 1; end if; if(event2 = '1') then test_int <= 2; end if; if(event3 = '1') then test_int <= 3; end if; end if; end process; If event1, event2 and event3 are all high when the clock edge occurs, is test_int guaranteed to be set to 3 due to this being the last assignment in the process? I am using Quartus II targeting a Cyclone device. There are no errors or warnings given, I just want to make sure the design is not relying on undefined behaviour. Thanks R.
Precedence of signal assignment in a clocked process
Started by ●February 6, 2009
Reply by ●February 6, 20092009-02-06
On 6 =AAubat, 04:36, n...@rblack01.plus.com wrote:> Hi, > > Say I have a register described by a clocked process: > > signal test_int : integer; > signal event1 : std_logic; > signal event2 : std_logic; > signal event3 : std_logic; > > process(clk) begin > =A0 =A0 =A0 =A0 if(clk'event and clk=3D'1') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if(event1 =3D '1') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 test_int <=3D 1; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end if; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if(event2 =3D '1') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 test_int <=3D 2; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end if; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 if(event3 =3D '1') then > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 test_int <=3D 3; > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 end if; > =A0 =A0 =A0 =A0 end if; > end process; > > If event1, event2 and event3 are all high when the clock edge occurs, > is test_int guaranteed to be set to 3 due to this being the last > assignment in the process? =A0 > > I am using Quartus II targeting a Cyclone device. =A0There are no errors > or warnings given, I just want to make sure the design is not relying > on undefined behaviour. > > Thanks > > R.if all the signals are high at the same time "last if" executes. Becasue all the signal assignments in the process are sequential. In order to be "more sure" try the syntax "elsif". --enes
Reply by ●February 6, 20092009-02-06
news@rblack01.plus.com wrote:> If event1, event2 and event3 are all high when the clock edge occurs, > is test_int guaranteed to be set to 3 due to this being the last > assignment in the process?Yes, at the next clock edge. If event 3 is active, then the value of 1 and 2 don't matter. -- Mike
Reply by ●February 6, 20092009-02-06
On Fri, 06 Feb 2009 12:36:49 +0000, news@rblack01.plus.com wrote:>Say I have a register described by a clocked process: > >signal test_int : integer; >signal event1 : std_logic; >signal event2 : std_logic; >signal event3 : std_logic; > >process(clk) begin > if(clk'event and clk='1') then > if(event1 = '1') then > test_int <= 1; > end if; > if(event2 = '1') then > test_int <= 2; > end if; > if(event3 = '1') then > test_int <= 3; > end if; > end if; >end process; > >If event1, event2 and event3 are all high when the clock edge occurs, >is test_int guaranteed to be set to 3 due to this being the last >assignment in the process?As Mike and Enes have already confirmed, the answer is "yes". Just to enlarge on this a little: When you make a <= signal assignment in VHDL, you are scheduling - planning - an update to the signal for some time in the future. A simple assignment s <= e; evaluates expression "e" immediately, and then schedules an assignment of that value on to signal "s" for a time one delta cycle into the future. From an RTL/design perspective, you can think of that delta cycle as being "just after the clock edge". s <= e after 5 ns; evaluates "e" immediately, but schedules the update of "s" for 5 ns into the future. Of course you can't meaningfully do this for a synthesizable design, because synthesis does not know how to make time delays reliably, so it will be treated exactly the same as the zero-delay assignment for synthesis (and you may get a warning message). But what happens when you make more than one assignment to the same signal, as in your example? The answer is quite complicated to describe, but has simple and intuitive effects: When you execute the signal assignment, it schedules an update to the signal at some future time T, which can be anything greater than or equal to (NOW + 1 delta). This new scheduled update affects any existing scheduled updates as follows: 1) Any existing scheduled updates at times >= T are cancelled. This is the behaviour that your code depends on. 2) Working backwards from time T towards NOW, VHDL inspects each scheduled update to "s". If it is to EXACTLY THE SAME VALUE as your new update, the existing earlier scheduled update is preserved. But as soon as VHDL encounters an update to a different value, it cancels that and all earlier updates. The second behaviour has the interesting effect that repeated attempts to bring a signal to a certain value after a time delay will indeed succeed. Consider this: for i in 1 to 10 loop s <= K after 10 ns; -- K is the same every time. wait for 5 ns; end loop; Suppose you start this loop at time 0; when will "s" change its value to K? Answer: at time 10 ns. The second assignment, executed at time 5 ns and scheduled for 15 ns, does NOT cancel the 10 ns schedule because it's to the same value. If you don't want a new assignment to affect existing already-scheduled updates, you can use transport delay: s <= transport e after 10 ns; This form of assignment creates the new schedule without affecting previous scheduled assignments; in other words, it obeys my rule (1) but it ignores rule (2). Of course, most of this stuff makes sense only for simulation, not for synthesis. But it justifies doing multiple assignments to the same signal within a synthesizable process; it is completely certain that only the last-executed assignment will take effect. -- Jonathan Bromley, Consultant DOULOS - Developing Design Know-how VHDL * Verilog * SystemC * e * Perl * Tcl/Tk * Project Services Doulos Ltd., 22 Market Place, Ringwood, BH24 1AW, UK jonathan.bromley@MYCOMPANY.com http://www.MYCOMPANY.com The contents of this message may contain personal views which are not the views of Doulos Ltd., unless specifically stated.
Reply by ●February 6, 20092009-02-06
news@rblack01.plus.com wrote:> Say I have a register described by a clocked process:(snip) > If event1, event2 and event3 are all high when the clock edge occurs, > is test_int guaranteed to be set to 3 due to this being the last > assignment in the process? As others have noted, the answer is yes. The result is more complicated logic than would otherwise be needed. If you don't need that complication, then you are getting bigger and slower logic than you would otherwise have. Not so bad at three levels, but it gets worse as it gets bigger. (This is a priority encoder.) If you need many levels, you can nest priority encoders, reducing the number of levels of logic needed to get the result. (A little similar to carry lookahead logic.) As far as I know, the synthesis tools won't generate a nested priority encoder for you.> process(clk) begin > if(clk'event and clk='1') then > if(event1 = '1') then > test_int <= 1; > end if; > if(event2 = '1') then > test_int <= 2; > end if; > if(event3 = '1') then > test_int <= 3; > end if; > end if; > end process;-- glen
Reply by ●February 16, 20092009-02-16
On Fri, 06 Feb 2009 17:43:21 +0000, Jonathan Bromley <jonathan.bromley@MYCOMPANY.com> wrote:>On Fri, 06 Feb 2009 12:36:49 +0000, news@rblack01.plus.com wrote: > >>Say I have a register described by a clocked process: >> >>signal test_int : integer; >>signal event1 : std_logic; >>signal event2 : std_logic; >>signal event3 : std_logic; >> >>process(clk) begin >> if(clk'event and clk='1') then >> if(event1 = '1') then >> test_int <= 1; >> end if; >> if(event2 = '1') then >> test_int <= 2; >> end if; >> if(event3 = '1') then >> test_int <= 3; >> end if; >> end if; >>end process; >> >>If event1, event2 and event3 are all high when the clock edge occurs, >>is test_int guaranteed to be set to 3 due to this being the last >>assignment in the process? > >As Mike and Enes have already confirmed, the answer is "yes". > >Just to enlarge on this a little: When you make >a <= signal assignment in VHDL, you are scheduling - >planning - an update to the signal for some time in >the future. A simple assignment > > s <= e; > >evaluates expression "e" immediately, and then schedules >an assignment of that value on to signal "s" for a time >one delta cycle into the future. From an RTL/design >perspective, you can think of that delta cycle as being >"just after the clock edge". > > s <= e after 5 ns; > >evaluates "e" immediately, but schedules the update of >"s" for 5 ns into the future. Of course you can't >meaningfully do this for a synthesizable design, >because synthesis does not know how to make time delays >reliably, so it will be treated exactly the same as >the zero-delay assignment for synthesis (and you may >get a warning message). > >But what happens when you make more than one assignment >to the same signal, as in your example? The answer is >quite complicated to describe, but has simple and >intuitive effects: > >When you execute the signal assignment, it schedules an >update to the signal at some future time T, which can >be anything greater than or equal to (NOW + 1 delta). >This new scheduled update affects any existing scheduled >updates as follows: > >1) Any existing scheduled updates at times >= T are > cancelled. This is the behaviour that your code > depends on. >2) Working backwards from time T towards NOW, VHDL > inspects each scheduled update to "s". If it is > to EXACTLY THE SAME VALUE as your new update, > the existing earlier scheduled update is preserved. > But as soon as VHDL encounters an update to a > different value, it cancels that and all earlier > updates. > >The second behaviour has the interesting effect that >repeated attempts to bring a signal to a certain value >after a time delay will indeed succeed. Consider this: > > for i in 1 to 10 loop > s <= K after 10 ns; -- K is the same every time. > wait for 5 ns; > end loop; > >Suppose you start this loop at time 0; when will "s" >change its value to K? Answer: at time 10 ns. The >second assignment, executed at time 5 ns and scheduled >for 15 ns, does NOT cancel the 10 ns schedule because >it's to the same value. > >If you don't want a new assignment to affect existing >already-scheduled updates, you can use transport delay: > > s <= transport e after 10 ns; > >This form of assignment creates the new schedule without >affecting previous scheduled assignments; in other words, >it obeys my rule (1) but it ignores rule (2). > >Of course, most of this stuff makes sense only for >simulation, not for synthesis. But it justifies >doing multiple assignments to the same signal within >a synthesizable process; it is completely certain >that only the last-executed assignment will take effect.OK, thanks, very detailed explanation! This behaviour is what I was hoping for. The real code I am working on has a much more complicated if-else structure inside the process, I try to keep all operations that modify a register inside the same clocked process. So if several different events can modify the register, I can force a particular priority on them - the last assignment in the process has highest priority.
Reply by ●February 16, 20092009-02-16
On Feb 16, 11:28=A0am, n...@rblack01.plus.com wrote:> On Fri, 06 Feb 2009 17:43:21 +0000, Jonathan Bromley > <jonathan.brom...@MYCOMPANY.com> wrote: > >On Fri, 06 Feb 2009 12:36:49 +0000, n...@rblack01.plus.com wrote: > > >>Say I have a register described by a clocked process: > > >>signal test_int : integer; > >>signal event1 : std_logic; > >>signal event2 : std_logic; > >>signal event3 : std_logic; > > >>process(clk) begin > >> =A0 =A0 =A0 =A0if(clk'event and clk=3D'1') then > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(event1 =3D '1') then > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0test_int <=3D 1; > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end if; > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(event2 =3D '1') then > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0test_int <=3D 2; > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end if; > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0if(event3 =3D '1') then > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0test_int <=3D 3; > >> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0end if; > >> =A0 =A0 =A0 =A0end if; > >>end process; > > > OK, thanks, very detailed explanation! =A0This behaviour is what I was > hoping for. > The real code I am working on has a much more complicated if-else > structure inside the process, I try to keep all operations that modify > a register inside the same clocked process. =A0So if several different > events can modify the register, I can force a particular priority on > them - the last assignment in the process has highest priority.I'm curious, why would you use a construct that you need to ask help on when you could easily use a nested IF ELSIF structure and make it perfectly clear what is intended to yourself as well as any other designers who need to maintain your code. Don't get me wrong, I'm not saying the above is a bad way to code. But obviously it is a little less clear. For example, even if you understand the VHDL rules governing this, it is not clear to someone else if it is *possible* for event1, event2 and event3 to be active at the same time. So if someone else has to change the code, they don't know if you are counting on the priority of the events or if it does not matter. If you use IF ELSIF ELSEIF THEN, you will be clearly indicating that you inend to enforce a priority on the assignments. Not only that, it is fewer lines of code so it will be easier to debug... ;^) Just a thought. Rick
Reply by ●February 17, 20092009-02-17
On Mon, 16 Feb 2009 16:38:45 -0800 (PST), rickman <gnuarm@gmail.com> wrote:> >I'm curious, why would you use a construct that you need to ask help >on when you could easily use a nested IF ELSIF structure and make it >perfectly clear what is intended to yourself as well as any other >designers who need to maintain your code. Don't get me wrong, I'm not >saying the above is a bad way to code. But obviously it is a little >less clear. For example, even if you understand the VHDL rules >governing this, it is not clear to someone else if it is *possible* >for event1, event2 and event3 to be active at the same time. So if >someone else has to change the code, they don't know if you are >counting on the priority of the events or if it does not matter. If >you use IF ELSIF ELSEIF THEN, you will be clearly indicating that you >inend to enforce a priority on the assignments. Not only that, it is >fewer lines of code so it will be easier to debug... ;^) > >Just a thought. > >RickOK, here is the relevant bit of the real code, it's part of a custom UART which does some de-multiplexing of the received data into 2 different receive buffers: -- Receive FIFOs & overrun flags -- Overrun flag set if write attempt made when FIFO full, cleared after reading status register -- If overrun flag set and reset events occur in the same clock cycle, set takes priority over reset -- to ensure overrun event is always detected p_rx_fifo : process(master_clk) begin if(master_clk'event and master_clk = '1') then if(sts_reg_rd_stb = '1') then rx_fifo_ovrun <= '0'; n_rx_fifo_ovrun <= '0'; end if; if(rx_done_stb = '1') then if((tx_sm = tsm_wait_rcv1) or (tx_sm = tsm_wait_rcv2)) then -- NRSA Rx FIFO if(n_rx_fifo_full = '0') then n_rx_fifo_wr_stb <= '1'; else n_rx_fifo_ovrun <= '1'; end if; else -- User Rx FIFO if(rx_fifo_full = '0') then rx_fifo_wr_stb <= '1'; else rx_fifo_ovrun <= '1'; end if; end if; else rx_fifo_wr_stb <= '0'; n_rx_fifo_wr_stb <= '0'; end if; end if; end if; end process; I coudn't see any other way of 'decoupling' the set and clear operations for the overrun flags. The 'set' path through the code only occurs if a new character has been received, the 'clear' path only occurs if the status register is read. The process is also deciding which FIFO to put the received character in, based on the state machine tx_sm, and generating the write strobes for the FIFOs. There's probably a neater way of doing this, I'm a relative newcomer to VHDL. Regards R.
Reply by ●February 17, 20092009-02-17
On Feb 17, 3:45 am, n...@rblack01.plus.com wrote:> On Mon, 16 Feb 2009 16:38:45 -0800 (PST), rickman <gnu...@gmail.com> wrote: > > >I'm curious, why would you use a construct that you need to ask help > >on when you could easily use a nested IF ELSIF structure and make it > >perfectly clear what is intended to yourself as well as any other > >designers who need to maintain your code. Don't get me wrong, I'm not > >saying the above is a bad way to code. But obviously it is a little > >less clear. For example, even if you understand the VHDL rules > >governing this, it is not clear to someone else if it is *possible* > >for event1, event2 and event3 to be active at the same time. So if > >someone else has to change the code, they don't know if you are > >counting on the priority of the events or if it does not matter. If > >you use IF ELSIF ELSEIF THEN, you will be clearly indicating that you > >inend to enforce a priority on the assignments. Not only that, it is > >fewer lines of code so it will be easier to debug... ;^) > > >Just a thought. > > >Rick > > OK, here is the relevant bit of the real code, it's part of a custom UART > which does some de-multiplexing of the received data into 2 different receive > buffers: > > -- Receive FIFOs & overrun flags > -- Overrun flag set if write attempt made when FIFO full, cleared after reading status register > -- If overrun flag set and reset events occur in the same clock cycle, set takes priority over reset > -- to ensure overrun event is always detected > p_rx_fifo : process(master_clk) begin > if(master_clk'event and master_clk = '1') then > if(sts_reg_rd_stb = '1') then > rx_fifo_ovrun <= '0'; > n_rx_fifo_ovrun <= '0'; > end if; > if(rx_done_stb = '1') then > if((tx_sm = tsm_wait_rcv1) or (tx_sm = tsm_wait_rcv2)) then > -- NRSA Rx FIFO > if(n_rx_fifo_full = '0') then > n_rx_fifo_wr_stb <= '1'; > else > n_rx_fifo_ovrun <= '1'; > end if; > else > -- User Rx FIFO > if(rx_fifo_full = '0') then > rx_fifo_wr_stb <= '1'; > else > rx_fifo_ovrun <= '1'; > end if; > end if; > else > rx_fifo_wr_stb <= '0'; > n_rx_fifo_wr_stb <= '0'; > end if; > end if; > end if; > end process; > > I coudn't see any other way of 'decoupling' the set and clear operations for the overrun flags. > The 'set' path through the code only occurs if a new character has been received, the > 'clear' path only occurs if the status register is read. The process is also deciding which > FIFO to put the received character in, based on the state machine tx_sm, and generating the > write strobes for the FIFOs. > > There's probably a neater way of doing this, I'm a relative newcomer to VHDL.I can't say if there is a neater way to code this or not. But the if (sts_reg_rd_stb = '1') could be put in the ELSE part of the if (rx_done_stb = '1') structure. I'm not completely clear on what the code is doing, so I can't say if there is a better way to structure it. After looking at it for a bit, it actually looks fairly clear to me. I would move the first IF (lowest priority) to be an ELSIF to make that clear. Also, I just think it reads more clearly showing it in a single unified structure rather than using two structures and then having to rely on the order to create the final level of priority. Also, some comments might help. For example, why is tx_sm being checked in what appears to be the receiver code??? Rick
Reply by ●February 17, 20092009-02-17
On Tue, 17 Feb 2009 08:45:09 +0000, news@rblack01.plus.com wrote:>OK, here is the relevant bit of the real code, it's part of a custom UART >which does some de-multiplexing of the received data into 2 different receive >buffers:It's not particularly bad code - one abstraction I would be tempted to make is to abstract out this logic into a procedure local to the process... procedure set_wr_strobe (signal fifo_full : std_logic; OUT fifo_wr_stb, fifo_ovrun : std_logic) is begin> if(fifo_full = '0') then > fifo_wr_stb <= '1'; > else > fifo_ovrun <= '1'; > end if;end procedure set_wr_strobe;>-- to ensure overrun event is always detected > p_rx_fifo : process(master_clk) begin > if(master_clk'event and master_clk = '1') then > if(sts_reg_rd_stb = '1') then > rx_fifo_ovrun <= '0'; > n_rx_fifo_ovrun <= '0'; > end if; > if(rx_done_stb = '1') then > if((tx_sm = tsm_wait_rcv1) or (tx_sm = tsm_wait_rcv2)) then > -- NRSA Rx FIFOset_wr_strobe (n_rx_fifo_full, n_rx_fifo_wr_stb, n_rx_fifo_ovrun);> else > -- User Rx FIFOset_wr_strobe (rx_fifo_full, rx_fifo_wr_stb, rx_fifo_ovrun);> end if; > else > rx_fifo_wr_stb <= '0'; > n_rx_fifo_wr_stb <= '0'; > end if; > end if; > end if; > end process; >But then I might be caught by a bug in XST which obeys variable assignment rules for those OUT mode signals. (Which may not matter if those signals aren't read in this same process). I really hope THIS one gets fixed in ISE 11... I believe that tool defects and the need to work around them is doing more to hold back progress in using VHDL at a higher level than anything in the language itself. - Brian






