I am trying to output one bit at a time via SPI from a know 2D array.
logic [7:0] fpga_status_queue [0:17],
My state machine is for some reason going to a weird state.
18'h
Here is my code:
module FPGA_STATUS_READ (
input clk,
input sclk,
input cs, // Clock Enable
input rst_n, // Asynchronous reset active low
input fpgastatus_command,
input logic [3:0] group_control,
input logic [7:0] loopback,
output logic [7:0] fpga_status_queue [0:17],
output logic dout
);
// `include "../../config_files/rtl_config.svh"
assign fpga_status_queue[ 0] = 8'h1;//{group_control};
assign fpga_status_queue[ 1] = 8'h1;//{loopback};
assign fpga_status_queue[ 2] = 8'h1;//{synth_version[0]};
assign fpga_status_queue[ 3] = 8'h1;//{synth_version[1]};
assign fpga_status_queue[ 4] = 8'h1;//{synth_version[2]};
assign fpga_status_queue[ 5] = 8'h1;//{grid_version[0]};
assign fpga_status_queue[ 6] = 8'h1;//{grid_version[1]};
assign fpga_status_queue[ 7] = 8'h1;//{grid_version[2]};
assign fpga_status_queue[ 8] = 8'h1;//{pa_version[0]};
assign fpga_status_queue[ 9] = 8'h1;//{pa_version[1]};
assign fpga_status_queue[10] = 8'h1;//{pa_version[2]};
assign fpga_status_queue[11] = 8'h1;//{hdl_version[0]};
assign fpga_status_queue[12] = 8'h1;//{hdl_version[1]};
assign fpga_status_queue[13] = 8'h1;//{hdl_version[2]};
assign fpga_status_queue[14] = '1;
assign fpga_status_queue[15] = '1;
assign fpga_status_queue[16] = '0;
assign fpga_status_queue[17] = '0;
logic bit_run_counter;
logic bit_load_counter;
logic [4:0] bit_current_value;
logic bit_count_reached;
logic word_run_counter;
logic word_load_counter;
logic [4:0] word_current_value;
logic word_count_reached;
typedef enum logic [2:0] {IDLE, CS_WAIT, MISO_OUT, BIT_CHANGE, WORD_CHANGE} status_states;
status_states current_state, next_state;
always_ff #(posedge clk or negedge rst_n) begin : step_forward
if(!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end : step_forward
always_comb begin : set_next_state
next_state = IDLE;
case (current_state)
IDLE : next_state = fpgastatus_command ? CS_WAIT : IDLE;
CS_WAIT : next_state = ~cs ? MISO_OUT : CS_WAIT;
MISO_OUT : begin//next_state = sclk ? bit_count_reached ? word_count_reached ? IDLE: WORD_CHANGE : BIT_CHANGE: MISO_OUT;
if (sclk && bit_count_reached && word_count_reached)
next_state = IDLE;
else if (sclk && bit_count_reached)
next_state = WORD_CHANGE;
else if (sclk)
next_state = BIT_CHANGE;
else
next_state = MISO_OUT;
end
BIT_CHANGE : next_state = MISO_OUT;
WORD_CHANGE: next_state = MISO_OUT;
default : next_state = IDLE;
endcase
end
always_comb begin : cntr_logic
bit_run_counter = '0;
bit_load_counter = '0;
word_run_counter = '0;
word_load_counter = '0;
dout = '0;
unique case (current_state)
IDLE :begin
bit_load_counter = '1;
word_load_counter = '1;
end
CS_WAIT :begin
bit_load_counter = '1;
word_load_counter = '1;
end
MISO_OUT :begin
dout = fpga_status_queue[word_current_value][bit_current_value];
end
BIT_CHANGE :begin
bit_run_counter = '1;
end
WORD_CHANGE:begin
word_run_counter = '1;
bit_load_counter = '1;
end
default : dout = '0;
endcase
end
up_down_counter #(
.ABSOLUTE_DATA_WIDTH(4)
) inst_bit_counter (
.clk (clk),
.run_counter (bit_run_counter),
.rst_n (rst_n),
.count_value (5'h7),
.load_counter (bit_load_counter),
.up_counter ('0),
.current_value (bit_current_value),
.count_reached (bit_count_reached)
);
up_down_counter #(
.ABSOLUTE_DATA_WIDTH(5)
) inst_word_counter (
.clk (clk),
.run_counter (word_run_counter),
.rst_n (rst_n),
.count_value (5'h11),
.load_counter (word_load_counter),
.up_counter ('0),
.current_value (word_current_value),
.count_reached (word_count_reached)
);
endmodule
It should go to WORD_CHANGE but both WORD_CHANGE and MISO_OUT for the next state for current state.
This is almost certainly a timing issue. I'm guessing that sclk is not synchronous to clk - probably it's connected directly to a device input pin.
The problem is this piece of code:
(...)
else if (sclk)
next_state = BIT_CHANGE;
else
next_state = MISO_OUT;
Whenever sclk transitions from zero to one, logic will raise the next_state bit corresponding to BIT_CHANGE, and in parallel lower the next_state bit corresponding to MISO_OUT. As this happens, there can be a brief moment where both bits are set or no bits are set, depending on which logic is faster. If you are unlucky and have a raising clk at this exact moment, you will get into the situation you are observing, where the state machine appears to be in two states at the same time.
The solution is to synchronize sclk, cs, and any other signals that determine the next state to clk. Such synchronization is typically done by simply sending the signals through two flip-flops.
Related
module count(clk,rst,cnt);
want to wrire the verilog code which counts upto 7 and then down to
0 and repeats forever as follows. 0,1,2,3,4,5,6,7,6,5,4,4,3,2,1
endmodule
Blockquote
Repeat state 4 for two times while counting down:
module count(
input clk,
input rst,
output cnt
);
reg [2:0] counter;
assign cnt = counter;
// Count up if flag is 1'b0, count down if flag is 1'b1
wire flag;
assign flag = (counter == 3'b1) ? 1'b1 : (counter == 3'b0) ? 1'b0;
// Repeat counter once if special_flag is 2'b01
reg [1:0] special_flag;
always #(posedge clk)
begin
if (flag == 1'b0)
begin
counter = counter + 1;
end
else if (counter == 3'b100 && special_flag == 2'b01)
begin
// Do not decrease counter, reset special_flag back to 2'b0
special_flag = 2'b0;
end else
begin
counter = counter - 1;
end
// Set special_flag to be 2'b01 when counter is 3'b1
if (counter == 3'b1)
begin
special_flag = 2'b01;
end
end
endmodule
module count(
input clk,
input rst,
output cnt
);
reg [2:0] counter;
assign cnt = counter;
// Count up if flag is 1'b0, count down if flag is 1'b1
wire flag;
assign flag = (counter == 3'b1) ? 1'b1 : (counter == 3'b0) ? 1'b0;
always #(posedge clk)
begin
if (flag == 1'b0)
begin
counter = counter + 1;
end
if (flag == 1'b1)
begin
counter = counter - 1;
end
end
endmodule
I'm creating a state machine with implicit datapath and am getting three errors that I haven't been able to resolve.
For the endcase error, I've made sure that all the begins have a corresponding end in the always block.
For the Finish error, the state has only been defined once so I'm not sure about that.
For the ; error, I have no idea why it doesn't want me to include countx and county statements.
Any help would be appreciated!
module fillscreen(input logic clk, input logic rst_n, input logic [2:0] colour,
input logic start, output logic done,
output logic [7:0] vga_x, output logic [6:0] vga_y,
output logic [2:0] vga_colour, output logic vga_plot);
enum logic [1:0] {Load = 2'b00, Increment = 2'b01, Out = 2'b10, Finish = 2'b11} state, next_state;
logic [7:0] countx, county;
always # (posedge clk) begin
case(state)
Load:
if(rst_n == 0)
next_state <= Load;
else if (start == 1)
next_state <= Increment;
else begin
next_state <= Load; end
//initialize counter
countx <= 0;
county <= 0;
Increment:
if(rst_n == 0)
next_state <= Load;
else if (county < 119 && countx < 159) begin
county <= county+1;
next_state <= Increment; end
else if (countx < 159) begin
countx <= countx +1;
next_state <= Increment; end
else
next_state <= Finish;
//output
vga_y <= county;
vga_x <= countx;
vga_colour <= countx % 8;
vga_plot <= 1;
Finish:
done <= 1;
if(rst_n == 0)
next_state <= Load;
else begin
next_state = Finish; end
Default:
vga_y <= county;
vga_x <= countx;
done <= 0;
vga_plot <= 0;
endcase
end
endmodule
Here are the errors I'm getting:
** Error: fillscreen.sv(22): near ";": syntax error, unexpected ';', expecting ':'
** Error: fillscreen.sv(54): near "endcase": syntax error, unexpected endcase
** Error: fillscreen.sv(25): 'Increment' already exists; must not be redefined as a named block
** Error fillscreen.sv(43): 'Finish' already exists; must not be redefined as a named block
For any case, you need to include begin..end if the code block for that case has multiple lines, just like if-statements or always blocks (see comments inline, there more than just missing begin..end):
case(state) // <- Note, you never assign state, only next_state, might want to review your code for correctness
Load: begin // <- This case has multiple lines
if (rst_n == 0) begin // <- I do begin..end for EVERYTHING as I inevitably come back and add lines in the body which can lead to bugs if there is no begin..end, like {..} in C
next_state <= Load;
end
else if (start == 1) begin
next_state <= Increment;
end
else begin
next_state <= Load;
end
//initialize counter
countx <= 0;
county <= 0;
end
Increment: begin
if (rst_n == 0) begin
next_state <= Load;
end
else if (county < 119 && countx < 159) begin
county <= county+1;
next_state <= Increment;
end
else if (countx < 159) begin
countx <= countx +1;
next_state <= Increment;
end
else begin
next_state <= Finish;
end
//output
vga_y <= county;
vga_x <= countx;
vga_colour <= countx % 8;
vga_plot <= 1;
end
Finish: begin
done <= 1;
if (rst_n == 0) begin
next_state <= Load;
end
else begin
next_state <= Finish; // Should be non-blocking
end
end
default: begin // <- Should be lower-case "default"
vga_y <= county;
vga_x <= countx;
done <= 0;
vga_plot <= 0;
end
endcase
I'm new to Verilog and it is maybe a dumb question but what is the preferred codeflow in Verilog to solve this problem:
Simple counter, counting external clk (INP) up to a particular value. If the counter matches the value it rises an output wire (DRDY) for one clk period then lowers it to 0. There is an external input (SR) where I'd like to set the comparison value, so if SR = 0, then the counting is up to 500000, if SR = 1 then up to 1000000. I can do it with one value, but I'd like to expand the functionality of my module.
Thank you in advance.
My code so far with one value comparison:
module ec(INP, RST, SR, DRDY, DRDY2);
input INP, RST, SR;
output reg DRDY, DRDY2;
reg [23:0] Q;
always #(posedge INP or negedge RST)
begin
if(!RST)
begin
Q <= 24'd0;
DRDY <= 1'b0;
end
else if( Q == 24'd1000000)
begin
Q <= 24'd0;
DRDY <= 1'b1;
DRDY2 <=~DRDY2;
end
else
begin
Q <= Q + 1;
DRDY <= 1'b0;
end
end
endmodule
An easy way to handle 2 options would be an expand the if statement:
always #(posedge INP or negedge RST) begin
if(!RST) begin
Q <= 24'd0;
DRDY <= 1'b0;
end
else if(
( (SR ==1'b0) && (Q == 24'd1000000) ||
(SR ==1'b1) && (Q == 24'd500000)
) begin
//...
end
else begin
//..
end
This can look quite messy in the code so could be separated out into a count target logic, if more options are to be supported then switch to a case statement instead of if.
reg [23:0] cnt_target ;
always #* begin
if (SR == 1'b1) begin
cnt_target = 24'd1000000 ;
else begin
cnt_target = 24'd500000 ;
end
end
always #(posedge INP or negedge RST) begin
if(!RST) begin
Q <= 24'd0;
DRDY <= 1'b0;
end
else if( Q == cnt_target) begin
//...
end
else begin
//..
end
NB: You might want to consider using Q >= cnt_target that way if SR changed on the fly you do not have to wait for Q to overflow. Plus >= for me tends to synthesis smaller than ==.
A couple of days ago I asked about a module (here) I wanted to implement which takes the MSB of input samples, accumulates them (by shifting) and combines them into the output sample when the 32 output bit is "filled".
Thanks to the help there, I got this implementation, which doesn't produce any compilation errors and seemed fine with Xilinx 12.1:
module my_rx_dsp0
#(
//frontend bus width
parameter WIDTH = 24
)
(
//control signals
input clock, //dsp clock
input reset, //active high synchronous reset
input clear, //active high on packet control init
input enable, //active high when streaming enabled
//user settings bus, controlled through user setting regs API
input set_stb, input [7:0] set_addr, input [31:0] set_data,
//full rate inputs directly from the RX frontend
input [WIDTH-1:0] frontend_i,
input [WIDTH-1:0] frontend_q,
//full rate outputs directly to the DDC chain
output [WIDTH-1:0] ddc_in_i,
output [WIDTH-1:0] ddc_in_q,
//strobed samples {I16,Q16} from the RX DDC chain
input [31:0] ddc_out_sample,
input ddc_out_strobe, //high on valid sample
output ddc_out_enable, //enables DDC module
//strobbed baseband samples {I16,Q16} from this module
output reg [31:0] bb_sample,
output reg bb_strobe //high on valid sample
);
reg [3:0] i_msb;
reg [3:0] q_msb;
reg [31:0] temp_buff = 0;
reg [1:0] count = 0;
always #(posedge clock) begin
if(ddc_out_strobe) begin
// bit shifter for MSB
temp_buff <= {i_msb,q_msb,temp_buff[31:8]};
// to avoid if-else condition
count <= (count==2'd3) ? 2'd0 : (count+1);
end
end
always #(*) begin
i_msb = ddc_out_sample[31:28];
q_msb = ddc_out_sample[15:12];
// to avoid if-else condition
bb_strobe = (count==2'd3);
bb_sample = bb_strobe ? temp_buff : 32'd0;
end
assign ddc_in_i = frontend_i;
assign ddc_in_q = frontend_q;
assign ddc_out_enable = enable;
endmodule //my_rx_dsp0_custom
Now I wanted to implement a testbench that tests my_rx_dsp0.v with some examples.
I implemented a my_rx_dsp0_tb_2.v, which reads 32 bit samples from a file named my_input.dat to feed to the module as inputs ddc_out_sample.
They are then compared to the correct values stored at my_output.dat.
Note: I did not write this testbench myself, I adapted it from another testbench from an open-source project.
Here is the implementation:
module my_rx_dsp0_tb ( );
reg clk;
reg reset;
reg enable;
reg ddc_out_strobe; //high on valid sample
reg [31:0] ddc_out_sample;
wire [31:0] bb_sample = 32'd0;
wire bb_strobe;
wire ddc_out_enable = 1'b1; //enables DDC module
parameter WIDTH = 24;
parameter clocks = 2; // number of clocks per input
reg endofsim = 0;
integer number_of_errors;
initial number_of_errors = 0;
wire set_stb = 1;
wire [7:0] set_addr;
wire [31:0] set_data;
wire [WIDTH-1:0] frontend_i;
wire [WIDTH-1:0] frontend_q;
wire [WIDTH-1:0] ddc_in_i;
wire [WIDTH-1:0] ddc_in_q;
reg signed [31:0] compare_out;
// Setup the clock
initial clk = 1'b0;
always #5 clk <= ~clk ;
// Come out of reset after a while
initial reset = 1'b1 ;
initial #1000 reset = 1'b0 ;
// Enable the entire system
initial enable = 1'b1 ;
// Instantiate UUT
my_rx_dsp0 #(.WIDTH(WIDTH)) UUT_rx_dsp0
( .clock(clk), .reset(reset), .clear(clear), .enable(enable),
.set_stb(set_stb), .set_addr(set_addr), .set_data(set_data),
.frontend_i(frontend_i), .frontend_q(frontend_q),
.ddc_in_i(ddc_in_i), .ddc_in_q(ddc_in_q),
.ddc_out_sample(ddc_out_sample), .ddc_out_strobe(ddc_out_strobe), .ddc_out_enable(ddc_out_enable),
.bb_sample(bb_sample), .bb_strobe(bb_strobe) );
//-------Setup file IO-------//
//
integer i, r_in, r_out, infile, outfile;
initial begin
infile = $fopen("my_input.dat","r");
outfile = $fopen("my_output.dat","r");
$timeformat(-9, 2, " ns", 10) ;
// for n=9,p=2 digits after decimal pointer
//min_field_width=10 number of character positions for %t
end
//-------Get sim values and display errors-------//
//
initial begin
// Initialize inputs
ddc_out_strobe <= 1'd0;
ddc_out_sample <= 32'd0;
// Wait for reset to go away
#(negedge reset) #0;
while(!endofsim) begin
// Write the input from the file or 0 if EndOfFile(EOF)
#(posedge clk) begin
#1
ddc_out_strobe <= 1'b1;
if(!$feof(infile))
r_in = $fscanf(infile,"%b\n",ddc_out_sample);
else
ddc_out_sample <= 32'd0;
end
//
// Clocked in; set the strobe to 0 if the # of clocks/sample
// is greater than 1
if( clocks > 1 ) begin
#(posedge clk) begin
ddc_out_strobe <= 1'b0 ;
end
// Wait for the specified # of cycles
for( i = 0 ; i < (clocks-2) ; i = i + 1 ) begin
#(posedge clk) #1 ;
end
end
//
//
// Print out the number of errors that occured
if(number_of_errors) begin
$display("FAILED: %d errors during simulation",number_of_errors) ;
end else begin
$display("PASSED: Simulation successful") ;
end
//
end
end
//-------Comparison btwn simulated values vs known good values-------//
//
always #(posedge clk) begin
if(reset)
endofsim <= 1'b0 ;
else begin
if(!$feof(outfile)) begin
if(bb_strobe) begin
r_out = $fscanf(outfile,"%b\n",compare_out);
if(compare_out != bb_sample) begin
$display("%t: %b != %b",$realtime,bb_sample,compare_out);
number_of_errors = number_of_errors + 1;
end else begin
$display("%t: %b = %b",$realtime,bb_sample,compare_out);
end
end
end else begin
// Signal end of simulation when no more outputs
endofsim <= 1'b1 ;
end
end
end
endmodule // my_rx_dsp0_tb
When simulating with ISim from Xilinx ISE Suite Edition 12.1 I do not get the desired functionality from the module. I am afraid the output contains several x states (unknown states), instead of 1s or 0s as expected.
Question Is this due to:
1) The way the files are being read with $fscanf?
2) Did I wrong by initializing reg [31:0] temp_buff = 0?
3) Or does someone have an idea on what went wrong?
The error prompts from the testbench are (as an example):
xx000x00xxx00x0xx000x0x000000000 != 10000110111001011100010001101100
The X is from having multiple conflicting drivers on bb_sample and ddc_out_enable. The wire type merges the drivers, conflicting bit values of the same strength resolve as X.
UUT_rx_dsp0 is the intended diver. However you added and additional drivers from the way you declared your wires.
...
wire [31:0] bb_sample = 32'd0; // "= 32'd0" is a continuous driver
wire bb_strobe;
wire ddc_out_enable = 1'b1; // "= 1'd1" is a continuous driver
...
What you want is:
...
wire [31:0] bb_sample;
wire bb_strobe;
wire ddc_out_enable;
...
Correcting the above will resolve the X issue. Based on the example error it looks like are data miss matches. With the provided information, it is hard to tell it if it a test-bench or design issue. Could be just clock or propagation skew.
I'm having trouble implementing a controller block for an 8-bit multiplier. It works normally, but only if I turn the reset wire on, then off, such as in the following stimulus (which works fine):
`timescale 1ns / 100ps
module Controller_tb(
);
reg reset;
reg START;
reg clk;
reg LSB;
wire STOP;
wire ADD_cmd;
wire SHIFT_cmd;
wire LOAD_cmd;
Controller dut (.reset(reset),
.START(START),
.clk(clk),
.LSB(LSB),
.STOP(STOP),
.ADD_cmd(ADD_cmd),
.SHIFT_cmd(SHIFT_cmd),
.LOAD_cmd(LOAD_cmd)
);
always
begin
clk <= 0;
#25;
clk <= 1;
#25;
end
initial
begin
LSB <= 0;
START <= 0;
reset <= 1;
#55;
reset <= 0;
#10;
START <= 1;
#100;
START <= 0;
LSB <= 1;
#200;
#20;
#100;
end
initial
$monitor ("stop,shift_cmd,load_cmd, add_cmd: " , STOP,SHIFT_cmd,LOAD_cmd,ADD_cmd);
endmodule
Here's the simulation result for the working stimulus:
Now, when I set the reset to zero, without ever bringing it high, here's what happens:
Clearly, I'm using the reset wire to bring my Controller to the IDLE state. Here's the code for the controller block:
`timescale 1ns / 1ps
module Controller(
input reset,
input START,
output STOP,
input clk,
input LSB,
output ADD_cmd,
output SHIFT_cmd,
output LOAD_cmd
);
//Five states:
//IDLE : 000 , INIT: 001, TEST: 011, ADD: 010, SHIFT: 110
localparam [2:0] S_IDLE = 0;
localparam [2:0] S_INIT = 1;
localparam [2:0] S_TEST = 2;
localparam [2:0] S_ADD = 3;
localparam [2:0] S_SHIFT = 4;
reg [2:0] state,next_state;
reg [3:0] count;
// didn't assign the outputs to wire.. if not work, check this.
assign ADD_cmd = (state == S_ADD);
assign SHIFT_cmd = (state == S_SHIFT);
assign LOAD_cmd = (state == S_INIT);
assign STOP = (state == S_IDLE);
always #(*) begin
case(state)
S_INIT: begin
count = 3'b000;
end
S_SHIFT: begin
count = count + 1;
end
endcase
end
always #(*)
begin
next_state = state;
case (state)
S_IDLE: next_state = START ? S_INIT : S_IDLE;
S_INIT: next_state = S_TEST;
S_TEST: next_state = LSB ? S_ADD : S_SHIFT;
S_ADD: next_state = S_SHIFT;
S_SHIFT: next_state = (count == 8) ? S_IDLE : S_TEST;
endcase
end
always #(posedge clk)
begin
//state <= S_IDLE;
if(reset) state <= S_IDLE;
else state <= next_state;
end
reg [8*6-1:0] statename;
always #* begin
case( state )
S_IDLE: statename <= "IDLE";
S_INIT: statename <= "INIT";
S_TEST: statename <= "TEST";
S_ADD: statename <= "ADD";
S_SHIFT: statename <= "SHIFT";
default: statename <= "???";
endcase
end
endmodule
I don't know how to fix this. As you can see from the code above, there is a commented portion which is basically always initializing the state to IDLE. But even that doesn't work. Here's the simulation for the code above removing the comment from '//state <= S_IDLE;':
It's going into a different state than any listed above, and I have no idea why.
So I'd like to know:
Why is it going into an unknown state? Why doesn't my uncommented code work?
What can I change for it to work as I intend?
Your problem is that without a reset or initial value, state and next_state will be X. Your case statement assigning to statename will take the default branch and decode to ???. Since your process that assigns next_state does not handle cases where state is X it will get stuck in this state forever.
Your attempt to fix this will not work:
state <= S_IDLE;
if(reset) state <= S_IDLE;
else state <= next_state;
When reset is low you are making two assignments to state, the first as S_IDLE and the second as next_state. This is not a race condition. The Verilog standard states that:
Nonblocking assignments shall be performed in the order the statements were executed.
Since no re-ordering of the event queue occurs for sequential statements within a process this translates to last assignment wins. Therefore your state <= S_IDLE; is effectively optimised away since regardless of the value of reset the assignment will be overridden.
There are two ways you could fix this so that you don't need a reset:
1. Use the default clause to make your state machine safe
always #(*)
begin
next_state = state;
case (state)
S_IDLE: next_state = START ? S_INIT : S_IDLE;
S_INIT: next_state = S_TEST;
S_TEST: next_state = LSB ? S_ADD : S_SHIFT;
S_ADD: next_state = S_SHIFT;
S_SHIFT: next_state = (count == 8) ? S_IDLE : S_TEST;
default: next_state = S_IDLE;
endcase
end
This will ensure that your state-machine is 'safe' and drops into S_IDLE if state is a non-encoded value (including X).
2. Initialise the variable
reg [2:0] state = S_IDLE;
For some synthesis targets (e.g. FPGAs) this will initialise the register to a specific value and can be used alongside or instead of a reset (see Altera Documentation on power-up values).
A couple of general points:
Depending on your synthesis tool it may be better to use an enumeration rather than explicitly defining values for your states. This allows the tool to optimise based on the overall design or use a global configuration for encodings (for example safe, one-hot).
Using a reset registers holding state is standard practice so you should carefully consider whether you really want to avoid using a reset.
The uncommented code is an example of poor coding practice because you are making 2 nonblocking assignments to state in the same timestep. Synthesis linting tools are likely to warn you of this situation.
Since using a reset is a common, good practice, I don't think you need to fix anything.