Overview

Modules and port correspond to the fundamental code scope and the ability to connect two different blocks of codes in Verilog. Module is what you can call the "shell" of the verilog code. This module again has signals/pins which are called ports that are declared inside the module itself. These pins are a direct medium to connect different modules togather to create a larger functional block.

Keywords associated with modules & ports are listed below:-

module
endmodule
input
output
inout

A few other keywords associated with module concepts are listed below:-

generate
endgenerate
parameter
localparam
defparam

Theory Questions
    The Verilog module itself defines a static hardware structure, the behavior described within procedural blocks can be dynamic, depending on how signals and states change over time.
    Inout ports are bi directional , they can drive data in both directions in a bus, whereas input and output ports are uni-directional.
    A parameterized module in Verilog allows you to create flexible and reusable hardware designs by using parameters. Parameters are values that can be configured when the module is instantiated, making the module adaptable to different situations or design requirements without modifying the module's code.
    The generate construct in Verilog is a powerful feature that allows for conditional and iterative generation of hardware structures, making it highly important for creating flexible and scalable designs.
    parameter : Used for parameters that need to be configurable at module instantiation time. They can be overridden from outside the module, making them flexible for different use cases. localparam : Used for constants that should not be altered from outside the module. They are internal to the module and fixed once defined.
    Parameters provide flexibility and configurability in Verilog designs, but once set, their values are static and cannot be changed dynamically after module instantiation.
    Yes, The generate construct allows for conditional and iterative generation of hardware structures,"If" and "case" statements are often used within generate blocks to conditionally generate different hardware based on parameter values or other compile-time conditions.
    Yes, you can use loops inside a generate block in Verilog. The generate construct allows for the iterative generation of hardware structures based on parameters or compile-time conditions, and loops are a common way to achieve this. The most commonly used loop construct in Verilog's generate block is the for loop, but while and repeat loops are also supported, although less commonly used.
    Procedural blocks such as always, always_comb, always_latch,always_ff, initial and final are considered to follow sequential execution flow in verilog. The contents/statements inside these blocks would execute sequentially.
    Continuous assignments execute in parallel.
    No, parallel does not mean reue parallel. The simulation using the Verilog's concept of Scheduling Regions gives us the illusion of parallalism which the real world circuits follow.
    No, It's not possible to define a module inside a module in verilog.
    In Verilog, the generate block is primarily intended for use within modules or interfaces. It allows for conditional or iterative generation of hardware components based on parameters, which is useful for creating scalable and parameterizable designs. The generate block itself is not designed to be used outside of a module or interface directly.
    Yes, It is possible to have global parameters in verilog.
    In Verilog, the localparam keyword is used to declare parameters that are local to a module and cannot be overridden by module instances or external references. Once a localparam is declared, its value cannot be modified or changed after its initial declaration within that module.
Coding Questions
    module NbitAND#(parameter N=2)(input wire[N-1:0] ip,output op);
        assign op = &(ip);   // reduction AND operator
    endmodule
    
    module NbitNAND#(parameter M=2)(input wire[M-1:0]ip,output op);
       wire and_out;
       assign op = ~ and_out;
       NbitAND #(M) ANDgate(.ip(ip),.op(and_out));
    endmodule
    
    
    module test;
       bit[7:0] a,b;
       wire out_and,out_nand;
       
       
       NbitAND #(3) AND_3b (.ip(a[2:0]),.op(out_and));
       
       NbitNAND #(3) NAND_3b (.ip(b[2:0]),.op(out_nand));
       
       initial begin
           for(int i=0 ; i<8; i++)
           begin
              a  = i;
              b  = i;
              #10;
              $display($time," AND GATE:: input=%0b output=%0b",a,out_and);
              $display($time," NAND GATE:: input=%0b output=%0b",b,out_nand);
           end
           $finish;
       end
    
    endmodule
    
    module Mstage_siso#(parameter M=2)
    (
     input ip,
     output op,
     input clk,rst_n
    );
    
    reg [M-1:0]temp;
    
    assign op = temp[0] ;
    
    always @(posedge clk)
    begin
       if(!rst_n) //active_low rst
       begin
          temp <= 0;
       end
       else
       begin
           temp = temp >> 1;
           temp[M-1] <= ip;
       end
    
    end
    
    endmodule
    
    module test;
     
      reg clk=0;
      reg rst_n = 1;
      reg ip;
      wire op;
    
      Mstage_siso #(8) siso_8stage(ip,op,clk,rst_n);
    
      always clk = #(5) ~clk;
     
      initial begin
         @(negedge clk);
         rst_n <= 0;
         
         ip <= 0;  
         
         @(negedge clk);
         rst_n <= 1;
    
         for(int i=0 ; i<16 ; i++)
         begin
            @(posedge clk);
            ip = i[3:2];
         end
         #50;
         $finish;
      end
    
      initial begin
         $dumpfile("eg31.vcd");
         $dumpvars(0,test);
      end
    
    endmodule
    
    //code 2x1 mux
    module mux2x1 (input [1:0]ip,input sel ,output op);
    
    assign op = ip[sel];
    
    endmodule
    
    module Nx1_mux#(parameter N=4)
    (
     input [N-1:0] ip,
     input [$clog2(N)-1:0] sel,
     output op
    );
    
    wire [N-1:0]temp_out[$clog2(N)];
    wire [$clog2(N)-1:0] sel_t;
    
    assign op = temp_out[0][0];
    
    genvar gen_k;
    generate
       for(gen_k=0 ; gen_k < $clog2(N) ; gen_k++)
       begin
         assign sel_t[gen_k] = sel[($clog2(N)-1)-gen_k];
       end
    endgenerate
    
    genvar gen_i,gen_j;
    generate
       for(gen_j= N/2 ; gen_j > 0 ; gen_j = gen_j/2 )
       begin : gen_block1
          for(gen_i=0 ; gen_i<gen_j ; gen_i++)
          begin:gen_block2
             if(gen_j==N/2)
             begin
                mux2x1 u_2x1_mux(.ip(ip[gen_i*2+:2]),.sel(sel_t[$clog2(gen_j)]),.op(temp_out[$clog2(gen_j)][gen_i]));
             end
             else
             begin
                mux2x1 u_2x1_mux(.ip(temp_out[$clog2(gen_j)+1][(gen_i*2)+:2]),.sel(sel_t[$clog2(gen_j)]),.op(temp_out[$clog2(gen_j)][gen_i]));
             end
          end
       end
    endgenerate
    
    endmodule
    
    
    module test;
    
    reg [15:0]ip;
    reg [3:0] sel;
    wire op;
    
    Nx1_mux #(16) u_Nx1_mux (ip,sel,op);
    
    initial begin
       ip='h5a10;
       for(int i=0 ;i<16 ; i++)
       begin
          sel = i;
          #5;
          $display("sel=%0d op=%0b",sel,op);
       end
    end
    
    initial
    begin
       $dumpfile("eg32.vcd");
       $dumpvars(0,test);
    end
    
    endmodule
    
    //M bit N stage PISO
    
    module Mb_Nstage_PISO #(parameter M=4, N=8)
    (
     input clk,rst_n,en,
     input [M-1:0] ip,
     output op
    );
    
    reg [N-1:0]temp;
    
    assign op = temp[0];
    
    always@(posedge clk)
    begin
       if(!rst_n) //active low reset
       begin
          temp <= 0;
       end
       else if(en)
       begin
          temp[N-1:M] <= ip;
       end
       else
       begin
          temp = temp >> 1;
       end
    end
    
    endmodule
    
    
    module test;
      reg clk=0,en=0;
      reg rst_n = 1;
      reg [3:0]ip;
      wire op;
    
      Mb_Nstage_PISO #(4,8) u_4b_8stage_PISO(clk,rst_n,en,ip,op);
    
      always clk = #(5) ~clk;
     
      initial begin
         @(negedge clk);
         rst_n = 0;
         
         ip = 5;  
         
         @(negedge clk);
         rst_n = 1;
         
         @(posedge clk);
         en =1;
         for(int i=0 ; i<16 ; i++)
         begin
            @(posedge clk);
            en=0;
            @(posedge clk);
         end
         #50;
         $finish;
      end
    
      initial begin
         $dumpfile("eg33.vcd");
         $dumpvars(0,test);
      end
    
    endmodule
    
    //NX1 or 1XN mux-demux circuit
    
    module mux_demux#(parameter N=2 ,M="M")
    (
     input [N-1:0] ip,
     input [$clog2(N)-1:0] sel,
     output [N-1:0] op  
    );
    
    reg [N-1:0] op_t;
    
    assign op = op_t;
    
    always@(*)
    begin
       if(M=="M")// MUX
       begin
           op_t[0] = ip[sel];
       end
       else //DEMUX
       begin
          op_t = 0;
          op_t[sel] = ip[0];
       end
    end
    
    endmodule
    
    
    module test;
    
     reg [3:0]ip_m,ip_d;
     wire [3:0]op_m,_op_d;
     reg [1:0] sel_m,sel_d;
    
     mux_demux #(4,"M") u_mux (.ip(ip_m),.sel(sel_m),.op(op_m));
     mux_demux #(4,"D") u_demux (.ip(ip_d),.sel(sel_d),.op(op_d));
     
     initial begin
        ip_m = 'ha;
        ip_d[0] = 1;
        for(int i=0 ;i<4 ; i++)
        begin
            sel_m = i;
            sel_d = i;
            #5;
            $display("MUX :: ip=%0b sel=%0b op=%0b",ip_m,sel_m,op_m[0]);
            $display("DEMUX :: ip=%0b sel=%0b op=%0b",ip_d[0],sel_d,op_d);
        end
        $finish;
     end
    
    endmodule
    
    //N bit round robin arbiter
    
    module Nb_arbiter #(parameter N=4)
    (
     input clk,rst_n,
     input [N-1:0] req,
     output reg [N-1:0] grant
    );
    
    reg cycle_ip=1;
    reg [N-1:0] latch_grant=0;
    
    function bit can_grant(int index);
      if(cycle_ip==1)
      begin
           if(latch_grant[index]==0)
           begin
              can_grant = 1'b1;
           end
           else
           begin
              can_grant = 1'b0;
           end
      end
      else
      begin
          can_grant = 1'b1;
      end
    endfunction
    
    always@(posedge clk)
    begin
       if(!rst_n)
       begin
          grant<=0;
       end
       else
       begin
          for(int i=0 ; i<N ; i++)
          begin
             if(req[i] && grant[i]==0 && can_grant(i))
             begin
                cycle_ip <= 1;
                grant <= 0;
                grant[i] <= 1;
                latch_grant[i] <=1;
                if(i==N-1)
                begin
                   cycle_ip <= 0;
                   latch_grant <= 0;
                end
                break ;
             end
             else if(req[i] && grant[i]==0 && i==0)
             begin
                 latch_grant <= 0;
                 grant <= 0;
                 grant[i] <= 1;
                 latch_grant[i] <=1;
             end
             else
             begin
                grant <= 0;
             end
          end
       end
    end
    
    endmodule
    
    
    module test;
    
    reg clk=0,rst_n;
    reg [3:0]req;
    wire [3:0]grant;
    
    always clk = #5 ~clk;
    
    Nb_arbiter #(4) u_arbiter (clk,rst_n,req,grant);
    
    initial begin
       rst_n <= 0;
       @(posedge clk);
       @(negedge clk);
       rst_n <= 1;
       
       @(negedge clk);
       req <= 1;
       @(negedge clk);
       req <= 2;
       @(negedge clk);
       req <= 4;
       #100;
       $finish;
    end
    
    initial begin
       $dumpfile("eg35.vcd");
       $dumpvars(0,test);
    end
    
    
    endmodule
    
    //Nb M depth fifo
    
    module Nb_2_pow_Mdepth_fifo#(parameter N=2 , M=4)
    (
     input wr_clk,rd_clk,
     input rst_n,
     output full,empty,
     input wr_en,rd_en,
     input [N-1:0] wrdata,
     output reg [N-1:0] rddata
    );
    
    reg[N-1:0] fifo[2**M];
    integer wr_ptr,rd_ptr;
    
    assign full = (wr_ptr==(2**M));
    assign empty = (wr_ptr==0);
    
    always@(posedge wr_clk)
    begin
       if(!rst_n)
       begin
          wr_ptr=0;
          for(int i=0 ; i<2**M; i++)
          begin
             fifo[i] <= 0;
          end
       end
       else if(wr_en && full==0)
       begin
           fifo[wr_ptr] <= wrdata;
           wr_ptr <= wr_ptr +1;
       end
    end
    
    always@(posedge rd_clk)
    begin
       if(!rst_n)
       begin
          rd_ptr <= 0;
       end
       else if(rd_en && empty==0)
       begin
          rddata <= fifo[rd_ptr];
          rd_ptr <= rd_ptr+1;
          wr_ptr <= wr_ptr-1;
       end
    end
    
    endmodule
    
    module test;
    
    reg wr_clk=0,rd_clk=0,rst_n,wr_en,rd_en;
    reg[1:0] wrdata;
    wire [1:0] rddata;
    wire full,empty;
    
    Nb_2_pow_Mdepth_fifo #(2,2) u_fifo(wr_clk,rd_clk,rst_n,full,empty,wr_en,rd_en,wrdata,rddata);
    
    always wr_clk = #5 ~wr_clk;
    always rd_clk = #5 ~rd_clk;
    
    initial begin
      rst_n=0;
      @(negedge wr_clk);
      @(negedge wr_clk);
      rst_n=1;
      @(negedge wr_clk);
     
      wr_en=1;
      for(int i=0 ; i<4 ; i++)
      begin
         wrdata = i;
         @(negedge wr_clk);
      end
      wr_en=0;
     #20;
      rd_en=1;
      for(int i=0 ; i<5 ; i++)
      begin
         @(negedge wr_clk);
      end
      rd_en=0;
    #20;
      $finish;
    end
    
    initial begin
       $dumpfile("eg36.vcd");
       $dumpvars(0,test);
    end
    
    endmodule