I have a problem with this SystemVerilog code. Here is the code:
module mult ( multiplicand, multiplier, Product, clk, clear, Startm, endm );
input [31:0] multiplicand;
input [31:0] multiplier ;
input clk;
input clear;
input Startm;
output logic [63:0] Product;
output logic endm;
enum logic [1:0] { inicio, multiplicar, nao_multiplicar, fim } estados;
logic [1:0] state;
logic [31:0] mplier;
logic [31:0] mplier_aux;
logic [31:0] mcand ;
logic [31:0] mcand_aux;
logic [63:0] Prod ;
logic [63:0] Prod_aux;
logic [5:0] cont;
logic [5:0] cont_aux;
initial begin
mplier = multiplier;
mplier_aux = multiplier;
mcand = multiplicand;
mcand_aux = multiplicand;
Prod = 0;
Prod_aux = 0;
state = inicio;
cont = 0;
cont_aux = 0;
end
always_ff #( posedge clk )
begin
if( clear )
begin
state <= inicio;
end
else if ( Startm )
begin
case( state )
inicio :
begin
if( mplier[0] == 0 )
begin
state <= nao_multiplicar;
end
else if( mplier[0] == 1 )
begin
state <= multiplicar;
end
end
multiplicar :
begin
if( cont == 32 )
state <= fim;
else if( mplier[0] == 0 )
begin
state <= nao_multiplicar;
end
else if( mplier[0] == 1 )
begin
state <= multiplicar;
end
end
nao_multiplicar:
begin
if( cont == 32 )
state <= fim;
else if( mplier[0] == 0 )
begin
state <= nao_multiplicar;
end
else if( mplier[0] == 1 )
begin
state <= multiplicar;
end
end
fim:
begin
state <= inicio;
end
endcase
end
end
always_comb
begin
case(state)
inicio:
begin
mplier = multiplier;
mcand = multiplicand;
Prod = 0;
cont_aux = cont + 1;
cont = cont_aux;
end
multiplicar:
begin
mcand_aux = mcand << 1;
mcand = mcand_aux ;
mplier_aux = mplier >> 1;
mplier = mplier_aux ;
Prod_aux = Prod + mcand;
Prod = Prod_aux;
cont_aux = cont + 1;
cont = cont_aux;
end
nao_multiplicar:
begin
cont_aux = cont + 1;
cont = cont_aux;
end
fim:
begin
Product = Prod;
endm = 1;
end
endcase
end
endmodule
I'm trying write a multiplicator with inputs of 32 bits and a product of 64 bits using
the Booth's algorithm. This error occur:
always_comb construct does not infer purely combinational logic
Why does this happens?
When describing combinational logic in always blocks, you have to make sure that all your variables are assigned to a value in all paths in your code. Otherwise a latch will be inferred. It's easy to miss something like this in traditional always blocks, so the always_comb block was introduced in SystemVerilog to explicitly check for this.
In your case, you've a few buses which are not assigned values in each branch of the case statement, for example mcand hasn't a value assigned to it in branches nao_multiplicar and fim.
There are 2 solutions. First is to, well, assign to all your variables in all your code branches.
Another solution is to write 'default' values for all of the variables in the always_comb before the case statement. In this way, each variable will always be assigned to a value each time the always_comb block triggers, and there'll be no warnings. YOur case statement then only needs to deal with the variables that need to change:
always_comb
begin
// Defaults (I think I got them all)
mplier = multiplier;
mcand = multiplicand;
Prod_aux = 0;
Prod = 0;
cont_aux = 0;
cont = 0;
Product = 0;
endm = 0;
// Now override the defaults when appropriate
case(state)
inicio:
begin
mplier = multiplier;
mcand = multiplicand;
Prod = 0;
cont_aux = cont + 1;
cont = cont_aux;
end
multiplicar:
begin
mcand_aux = mcand << 1;
mcand = mcand_aux ;
mplier_aux = mplier >> 1;
mplier = mplier_aux ;
Prod_aux = Prod + mcand;
Prod = Prod_aux;
cont_aux = cont + 1;
cont = cont_aux;
end
nao_multiplicar:
begin
cont_aux = cont + 1;
cont = cont_aux;
end
fim:
begin
Product = Prod;
endm = 1;
end
endcase
end
All the compile errors are eliminated when I get rid of the initial block. I'm using simulators from Cadence and Synopsys.
Here is a quote from the IEEE Std, 1800-2009, section 9.2.2.4 "Sequential logic always_ff procedure":
The always_ff procedure imposes the
restriction that it contains one and
only one event control and no blocking
timing controls. Variables on the
left-hand side of assignments within
an always_ff procedure, including
variables from the contents of a
called function, shall not be written
to by any other process.
There is a similar quote for always_comb.
The documentation is readily available from the IEEE. Your simulator should also have documentation.
The error message you receive from your tool seems not to be very helpful in this case.
always_comb will infer combinational logic, however, in your always_comb block you are doing assignments like C code, e.g:
always_comb
begin
...
cont_aux = cont + 1;
cont = cont_aux;
...
end
Here you are asking to make a combinational logic with feedback.
If you want to store values in time, you have to put your assignments in an always_ff block, which will infer sequential logic.
always_ff #(posedge clk)
begin
...
cont <= cont + 1;
...
end
The LRM is the most useful documentation I use for system verilog
http://www.vhdl.org/sv/SystemVerilog_3.1a.pdf
Related
While I was trying to run the simulation in vivado, I got:
ERROR: Iteration limit 10000 is reached. Possible zero delay
oscillation detected where simulation time can not advance. Please
check your source code. Note that the iteration limit can be changed
using switch -maxdeltaid. Time: 10 ns Iteration: 10000
I don't have any initial statement in my module being tested.
Could anybody point out where the problem could be?
`timescale 1ns / 1ps
module mulp(
input clk,
input rst,
input start,
input [4:0] mplier, // -13
input [4:0] mplcant, // -9
output reg done,
output [9:0] product
);
parameter N = 6;
parameter Idle = 2'b00;
parameter Load = 2'b01;
parameter Oper = 2'b10;
parameter Finish = 2'b11;
reg done_r;
reg [N-1:0] A, A_r, B, B_r;
reg [1:0] state, state_r;
reg [2:0] count, count_r;
wire [N-2:0] C, C_comp;
reg [N-2:0] C_r;
assign C = mplcant; assign C_comp = {~C + 1};
assign product = {A_r[N-2:0], B_r[N-2:0]};
always #(posedge clk) begin
if (rst) begin
state_r <= Idle;
count_r <= 0;
done_r <= 0;
A_r <= 0;
B_r <= 0;
end else begin
state_r <= state;
count_r <= count;
done_r <= done;
A_r <= A;
B_r <= B;
end // if
end // always
always #(*) begin
state = state_r;
count = count_r - 1; // count: 6
done = done_r;
A = A_r;
B = B_r;
case (state)
Idle: begin
if (start) begin
state <= Load;
end // if
end
Load: begin
A = 0; B = {mplier, 1'b0}; count = N; // start at 6
state = Oper;
end
Oper: begin
if (count == 0)
state = Finish;
else begin
case (B[1:0])
2'b01: begin
// add C to A
A = A_r + {C[N-2], C[N-2:0]};
// shift A and B
A = {A_r[N-1], A_r[N-1:1]};
B = {A_r[0], B_r[N-1:1]};
end
2'b10: begin
A = A_r + {C_comp[N-2], C_comp[N-2:0]};
A = {A_r[N-1], A[N-1:1]};
B = {A_r[0], B_r[N-1:1]};
end
(2'b00 | 2'b11): begin
A = {A_r[N-1], A[N-1:1]};
B = {A_r[0], B_r[N-1:1]};
end
default: begin
state = Idle; done = 1'bx; // error
end
endcase
end // else
end // Oper
Finish: begin
done = 1;
state = Idle;
end // Finish
default: begin
done = 1'bx;
state = Idle;
end
endcase
end // always
endmodule
You have a combinational loop. You are sampling and driving the state signal in the combinational always block. Typically, you sample the registered state variable (state_r in your code) in an FSM. Change:
case (state)
to:
case (state_r)
Unrelated, but you should use all blocking assignments in the combo block (not a mixture). Change:
state <= Load;
to:
state = Load;
Quartus is telling me that I have inferred latches for input_val, ii, output_val, delayed, and addr_to_rom. I have looked at previous posts and made changes so that all my if statements have an else component and that my case statement has a default option.
What else could be causing all of these inferred latches?
(updated code below)
module simple_fir(clk, reset_n, output_val);
parameter data_width = 8;
parameter size = 1024;
input wire clk;
input wire reset_n;
//input reg [(data_width):0] input_val;
reg [(data_width):0] input_val;
output reg [data_width:0] output_val;
reg [(data_width):0] delayed;
reg [data_width:0] to_avg;
reg [9:0] ii ;
reg [9:0] i ;
reg [data_width:0] val;
reg [data_width:0] output_arr [(size-1):0];
logic [(data_width):0] data_from_rom; //precalculated 1 Hz sine wave
logic [9:0] addr_to_rom;
initial delayed = 0;
initial i = 0;
initial ii = 0;
//port map to ROM
rom_data input_data(
.clk(clk),
.addr(addr_to_rom), //text file?
.data(data_from_rom)
);
//Moore FSM
localparam [3:0]
s0=0, s1 = 1, s2 = 2, s3 = 3, s4 = 4, s5 = 5, s6 = 6;
reg [3:0] state_reg, state_next;
initial state_next = 0;
always #(posedge clk, negedge reset_n) begin //posedge clk, reset_n
if (reset_n == 'b0) begin //reset is active low
state_reg <= s0;
end else begin
state_reg <= state_next;
end
end
always #* begin
state_next = state_reg; // default state_next
case (state_reg)
s0 : begin //initial state, reset state
if (!reset_n) begin
output_val = 0;
delayed = 0;
to_avg= 0;
i = 0;
ii = 0;
end else begin
state_next = s1;
end
end
s1 : begin
if (ii>(size-2)) begin
ii = 0;
end else begin
addr_to_rom = ii;
end
input_val = input_val;
delayed = delayed;
state_next = s2;
end
s2 : begin
input_val = data_from_rom;
ii = ii+1;
delayed = delayed;
state_next = s3;
end
s3 : begin
delayed = input_val;
state_next = s4;
end
s4 : begin
addr_to_rom = ii;
input_val = input_val;
delayed = delayed;
state_next = s5;
end
s5 : begin
input_val = data_from_rom;
delayed = delayed;
//i=i+1;
//ii <= ii+1;
state_next = s6;
end
s6 : begin
to_avg = input_val + delayed; //summing two values
val = (to_avg >> 1); //taking the average
output_arr[ii-1] = val; //indexing starts on [2]
output_val = val;
state_next = s0;
input_val = input_val;
end
default: begin
addr_to_rom = addr_to_rom;
data_from_rom = data_from_rom;
delayed = delayed;
input_val = input_val;
to_avg = to_avg;
val = val;
output_val = output_val;
ii = ii;
end
endcase
end
endmodule
You have 6 states in your combo always block, but you are assigning to delayed in only 3 of them. This implies that you want to retain state, which infers latches. You should make sure delayed is assigned some value in all states (and in all branches of if/else, if applicable.
Also, you have an incomplete sensitivity list. You likely want to change:
always #(state_reg) begin
to:
always #* begin
#* is the implicit sensitivity list, which triggers the always block whenever a change occurs to any signal on the RHS of an assignment.
you have to look at the ways state machines are programmed. Usually states are calculated using flop and the final assignment is a combinatorial. You have it vice versa. Therefore you have issues. Something like the following should work for you. Sorry, i did not test it. The code needs to use non-blocking assignments except in the place where you caculate an intermediate value.
always #*
state_reg = state_next;
always #(posedge clk, negedge reset_n) begin //posedge clk, reset_n
if (reset_n == 'b0) begin //reset is active low
state_next <= s0;
end
else begin
case (state_reg)
S0: begin
output_val <= 0;
delayed <= 0;
i <= 0;
ii <= 0;
end
s1 : begin
if (ii>(size-2)) begin
ii <= 0;
end else begin
addr_to_rom <= ii;
end
input_val <= input_val;
delayed <= delayed;
state_next <= s2;
end
s2 : begin
input_val <= data_from_rom;
ii <= ii+1;
state_next <= s3;
end
s3 : begin
delayed <= input_val;
state_next <= s4;
end
s4 : begin
addr_to_rom <= ii;
state_next <= s5;
end
s5 : begin
input_val <= data_from_rom;
state_next <= s6;
end
s6 : begin
// calculating intermediate value, need blocking assignments here
to_avg = input_val + delayed; //need blocking here
val = (to_avg >> 1); //need blocking here
output_arr[ii-1] <= val; //indexing starts on [2]
output_val <= val;
state_next <= s0;
end
endcase
end // else
end // always
I noticed you used a coding style that a default assignment state_next = state_reg is placed at the very top of the always block. That makes state_next always have a legal driver if it is not explicitly assigned. This is also required for other combinational signals. Otherwise, latch inferred. Besides, the signal data_from_rom, which I assume is an output from module rom_data, has mutiple drivers.
I have been trying to make an asynchronous fifo in verilog but I'm facing a problem of object "empty" and "full" on left side of assignment should have variable data type.
Top module:
module async_fifo (reset, wclock, rclock, datain, dataout, e, f);
input [15:0] datain;
output reg [15:0] dataout;
//reg [15:0] mem1, mem2, mem3, mem4, mem5, mem6, mem7, mem8;
reg [15:0] mem [7:0];
input reset, rclock, wclock;
/*reg [0:2] wptr, rptr;
initial wptr = 3'b000;
initial rptr = 3'b000;*/
integer wflag = 0, rflag = 0;
wire empty , full;
input e,f;
reg [0:2] wptr = 3'b000, rptr = 3'b000;
counter c(wclock, rclock, empty, full);
e = empty;
f = full;
always#(posedge wclock)
begin
if(f == 1'b0)
begin
e = 1'b0;
if (wptr < 3'b111)
begin
mem[wptr] = datain;
wptr = wptr + 3'b001;
end
else if(wptr == 3'b111 && wflag == 0)
wflag = 1;
else if (wflag == 1)
begin
wptr = 3'b000;
wflag = 0;
end
end
end
always#(posedge rclock)
begin
if(e == 1'b0)
begin
f = 1'b0;
if (rptr < 3'b111)
begin
dataout = mem[rptr];
rptr = rptr + 3'b001;
end
else if(rptr == 3'b111 && rflag == 0)
rflag = 1;
else if (rflag == 1)
begin
rptr = 3'b000;
rflag = 0;
end
end
end
endmodule
Counter module:
module counter(w_clock, r_clock, empty, full);
input w_clock, r_clock;
output reg empty = 0, full = 0;
integer rear = 0, front = 0;
always # (posedge w_clock)
begin
if ((front == 1 && rear == 8) || front == rear + 1)
full = 1;
else if(rear == 8)
begin
rear = 1;
empty = 0;
end
else
begin
rear = rear+1;
empty = 0;
end
end
always # (posedge r_clock)
begin
if (front == 0 && rear == 0)
empty = 1;
else if(front == 8)
begin
front = 1;
full = 0;
end
else
begin
front = front+1;
full = 0;
end
end
endmodule
You are using full and empty in left hand side of behavioral block (always). So they have to be registers.
But at the same time they are output of counter and have to be wires.
You can't use variables in that way.They can either be output of an instant and only used in right hand side of other parts of code or be regsietrs for using in left hand side of behavioral blocks that can also be input of another instant.
You better change your coding style.
Here is an examole of async FIFO:
http://www.asic-world.com/code/hdl_models/aFifo.v
And also you need to study about blocking & nonblocking assignment and race conditions. Take a look at this:
http://ee.hawaii.edu/~sasaki/EE361/Fall01/vstyle.txt
I can't figure out , where this errors.Invalid use of input signal <ck> as target error is coming from?
module register
#(parameter Width = 8)
(output reg [Width-1:0] out,
input [Width-1:0] in,
input clear, load, clock);
always #(posedge clock)
if (~clear)
out<= 0;
else if (~load)
out<=in;
endmodule
module adder
#(parameter Width = 8)
(input [Width-1:0] a,b,
output [Width-1:0] sum);
assign sum = a + b;
endmodule
module compareLT // compares a < b
#(parameter Width = 8)
(input [Width-1:0] a, b,
output out);
assign out = a < b;
endmodule
module compareLEQ // compares a <= b
#(parameter Width = 8)
(input [Width-1:0] a, b,
output out);
assign out = a <= b;
endmodule
module roshanpoop
#(parameter Width = 8)
(input ck, reset,
input [Width-1:0] yln,
output [Width-1:0] y, x);
wire [Width-1:0] i, addiOut, addxOut;
wire yLoad, yClear, xLoad, xClear, iLoad,iClear;
register #(Width) I (i, addiOut, iClear, iLoad, ck);
register #(Width) Y (y, yIn, yClear, yLoad, ck);
register #(Width) X (x, addxOut, xClear, xLoad, ck);
adder #(Width) addI (addiOut, 'b1, i),
addX (x, y, addxOut);
compareLT #(Width) cmpX (x, 'b0, xLT0);
compareLEQ #(Width) cmpI (i, 'd10, iLEQ10);
fsm ctl (xLT0,iLEQ10 ,yLoad, yClear, xLoad, xClear, iLoad,iClear, ck, reset);
endmodule
module fsm
(input LT,LEQ, ck, reset,
output reg yLoad, yClear, xLoad, xClear, iLoad, iClear);
reg [2:0] cState, nState;
always #(posedge ck,negedge reset)
if (~reset)
cState <= 0;
else
cState <= nState;
always#(cState, LT,LEQ)
case (cState)
3'b00: begin //stateA
yLoad = 1; yClear = 1; xLoad = 1; xClear = 0;
iLoad = 1; iClear = 0; nState = 3'b001;
end
3'b001: begin // state B
yLoad = 1; yClear = 1; xLoad = 0; xClear = 1;
iLoad = 0; iClear = 1; nState = 3'b010;
end
3'b010: begin //state C
yLoad = 1; yClear = 1; xLoad = 1; xClear = 1;
iLoad = 1; iClear = 1;
if(LEQ) nState = 3'b001;
if(~LEQ & LT) nState = 3'b011;
if (~LEQ & ~LT) nState = 3'b100;
end
3'b011: begin //state D
yLoad = 1; yClear = 0; xLoad = 1; xClear = 1;
iLoad = 1; iClear = 1; nState = 3'b101;
end
3'b100: begin //state E
yLoad = 1; yClear = 1; xLoad = 1; xClear = 0;
iLoad = 1; iClear = 1; nState = 3'b101;
end
default: begin // required to satisfy combinational synthesis rules
yLoad = 1; yClear = 1; xLoad = 1; xClear = 1;
iLoad = 1; iClear = 1;nState = 3'b000;
$display("Oops, unknown state: %b", cState);
end
endcase
endmodule
error:
line no:70
Invalid use of input signal ck as target,
Invalid use of input signal target as target.
In module roshanpoop above mentioned error are coming . what might be the problem ?
The error is caused by this instantiation:
fsm ctl (xLT0,iLEQ10 ,yLoad, yClear, xLoad, xClear, iLoad,iClear, ck, reset);
of the module:
module fsm
(input LT,LEQ, ck, reset,
output reg yLoad, yClear, xLoad, xClear, iLoad, iClear);
You are using positional instantiation, which is not recomended, because it makes the task of maintaining your module more difficult (think, for example, if you want to add signals to your module: if you add it in the middle of the module's definition, all remaining signals will be wrongly connected).
Here, the use of positional instantiation has caused signal ck from the top module to be connected to iLoad, which is an output signal from fsm, so you are trying to put a value to a input only signal ck.
The way to have it right is to use explicit instantiation, where each signal from the module is explicitly named and assigned to a signal from the top module, like this:
fsm ctl (.LT(xLT0),
.LEQ(iLEQ10),
.yLoad(yLoad),
.yClear(yClear),
.xLoad(xLoad),
.xClear(xClear),
.iLoad(iLoad),
.iClear(iClear),
.ck(ck),
.reset(reset)
);
So, regardless of where in the argument list you put signal clk it will be always connected to the right signal inside the module.
Not an answer but some tips on potentially making the code easier to understand. I would post as a comment but code examples do not work well in comments.
1) Manual sensitivity lists can [should] be avoided when using a modern toolset.
always#(cState, LT,LEQ)
With an automatic sensitivity list would just be:
always #(*)
// Or
always #*
If you are able to use SystemVerilog (as question tags indicate) then the preferred method is :
always_comb
2) Instead of:
yLoad = 1; yClear = 1; xLoad = 1; xClear = 0;
iLoad = 1; iClear = 0;
For every case, we could have
reg [5:0] temp_control;
assign {yLoad, yClear xLoad, xClear, iLoad, iClear} = temp_control;
//...
always #*
case(cState)
3'b000: begin //stateA
temp_control = 6'b111010; nState = 3'b001;
end
3'b001: begin // state B
temp_control = 6'b110101; nState = 3'b010;
end
3'b010: begin //state C
temp_control = 6'b111111;
if(LEQ) nState = 3'b001;
if(~LEQ & LT) nState = 3'b011;
if (~LEQ & ~LT) nState = 3'b100;
end
//...
Better still create Mnemonics for the temp_controls.
localparam [5:0] CTRL_LOAD = 6'b111010;
localparam [5:0] CTRL_CLEAR = 6'b111010;
Mnemonics for the states are also really helpful:
localparam [2:0] STATE_INIT = 3'b000;
localparam [2:0] STATE_START = 3'b001;
localparam [2:0] STATE_STOP = 3'b010;
The the FSM structure might look some thing like:
always #*
case(cState)
STATE_INIT: begin //stateA
temp_control = CTRL_LOAD; nState = STATE_START;
end
STATE_START: begin // state B
temp_control = CTRL_CLEAR; nState = STATE_STOP;
end
STATE_STOP: begin //state C
temp_control = CTRL_HALT;
if(LEQ) nState = STATE_START;
if(~LEQ & LT) nState = STATE_RECYCLE;
if (~LEQ & ~LT) nState = STATE_CRUSH;
end
As the readability improves it is often easier to spot an incorrectly used signal.
I have a homework problem where I'm supposed to create a module for single-precision IEEE-754 floating point multiplication. This is the module:
module prob3(a, b, s);
input [31:0] a, b; // operands
output reg [31:0] s; // sum - Could potentially use wire instead
integer i; // loop variable
reg [8:0] temp;
reg [47:0] intProd; // intermediate product
reg [23:0] tempA;
reg [23:0] tempB;
initial begin
//Initialization
for (i = 0; i < 48; i = i + 1) begin
intProd[i] = 0;
end
//Compute the sign for the result
if (a[31]^b[31] == 0) begin
s[31] = 0;
end
else begin
s[31] = 1;
end
//Compute the exponent for the result
#10 temp = a[30:23] + b[30:23] - 8'b11111111;
//Case for overflow
if(temp > 8'b11111110) begin
s[30:23] = 8'b11111111;
for (i = 0; i < 23; i = i + 1) begin
s[i] = 0;
end
$finish;
end
//Case for underflow
else if (temp < 8'b00000001) begin
for (i = 0; i < 31; i = i + 1) begin
s[i] = 0;
end
$finish;
end
else begin
s[30:23] = temp[7:0];
end
//Mutliply the signficands
//Make implicit one explicit
tempA[23] = 1;
tempB[23] = 1;
//Make operands 24 bits
for(i = 0; i < 23; i = i + 1) begin
tempA[i] = a[i];
tempB[i] = b[i];
end
//Compute product of signficands
intProd = tempA * tempB;
//Check and see if we need to normalize
if(intProd[47:46] >= 2'b10) begin
intProd = intProd >> 1;
temp = s[30:23] + 1'b1;
if(temp > 8'b11111110) begin
s[30:23] = 8'b11111111;
for (i = 0; i < 23; i = i + 1) begin
s[i] = 0;
end
$finish;
end
else
s[30:23] = temp[7:0];
end
s[22:0] = intProd[47:25];
end
endmodule
Here is my testbench:
module prob4;
reg [31:0] a, b;
wire [31:0] s;
// instantiate the floating point multiplier
prob3 f1(a, b, s);
initial begin
assign a = 32'h42055555;
assign b = 32'hBDCCCCCD;
#10 $monitor("s = %h", s);
assign a = 32'hBF555555;
assign b = 32'hCAB71B00;
#10 $monitor("s = %h", s);
a = 32'hFF500000;
b = 32'h7E700000;
#10 $display("s = %b", s);
a = 32'h01700000;
b = 32'h02F00000;
#10 $display("s = %b", s);
a = 32'hBE000000;
b = 32'h455F36DB;
#10 $display("s = %b", s);
a = 32'h3C800000;
b = 32'h3A800000;
#10 $display("s = %b", s);
a = 32'hC797E880;
b = 32'hB7FBA927;
#10 $display("s = %b", s);
end
endmodule
It displays the first value of s, but that is it. I'm honestly not too familiar with Verilog, so any clarification on why this might be happening would be truly appreciated.
The reason you are seeing only a single value for s is because all of your floating point logic (all the stuff in the prob3 module) is inside an initial block. Thus, you only run that code once; it starts at time 0, has a pause for 10 time units and finishes; never to run again. Here are a few tips for implementing the unit (assuming the module is suppose to be synthesizable and not just a functional verification model):
Place your combinational logic in an always #(*) block, not an initial block.
As toolic mentioned, only call $monitor once, and it will inform you whenever s or any other variables given as arguments change; thus you do not need the $display statements either unless you want to know the value of s at that point of execution (whether it changed or not and inline with the processes, so not necessarily the final value either). So typically your testbench main stimulus initial block would have $monitor() as the first line.
Don't call $finish inside your logic; ideally, you should set an error signal instead that the testbench might then choose to call $finish if it sees that error signal asserted.
Don't use assign inside procedural blocks (always, initial, etc), just say a = ... not assign a = ...