Assignment 6

Due Oct 29th

P1 Evaluate CORDIC implementations using Out of Context Synthesis

The first few pages of Chapter 15 discuss a CORDIC operation, which supports for instance computing sine. The input is an angle (360 degrees remapped to the input binary word integer range) and the outputs are normalized to the output word range. You don't need to know the theory behind it – the reference C code is the only specification you need right now.

C code based on Course Textbook Listing 15.1

#define AG_CONST 163008218

  static const int angles[] = {
  210828714, 124459457, 65760959, 33381289,
  16755421, 8385878, 4193962, 2097109,
  1048570, 524287, 262143, 131071,
  65535, 32767, 16383, 8191,
  4095, 2047, 1024, 511 };

  void cordic(int target, int *rX, int*rY) {
    int X, Y, T, current;
    unsigned step;

    X = AG_CONST;
    Y = 0;
    current = 0;
    for (step=0; step<20 ; step++){
      if (target > current) {
        T = X - (Y>>step);
        Y = (X >> step) + Y;
        X = T;
        current += angles[step];
      } else {
        T = X + (Y >> step);
        Y = -(X >> step) + Y;
        X = T;
        current -= angles[step];
      }
      printf("target:%x,current:%x,X:%x,Y:%x\n",target,current,X,Y); //this line added
    }
    *rX = X;
    *rY = Y;
  }


int main(){

  int x = 0,y=1;
  int test_targets[4];
  int target;
  test_targets[0] =  210828714;   // small positive
  test_targets[1] = -124459457;   // small negative
  test_targets[2] =  65760959;    // smaller positive
  test_targets[3] =  33381289;    // even smaller

  //cordic(int target, int *rX, int*rY)
  target = test_targets[0];
  cordic(target, &x, &y);
  printf("%x->%x,%x\n",target,x,y);

}

Reference Implementation and Testbench

Below is a fixed version of CORDIC from an a generative AI (2025 ChatGPT I think), based on that C code. Some AIs still haven't mastered use of blocking and non-blocking and need fixes like this and more

X /*T*/ <= X - (Y >>> step);
Y <= (X >>> step) + Y;
/*X <= T;*/

It could use more work, but here is the implementation and a testbench

//`timescale 1ns/1ps
// =============================================================
// Synthesizable Verilog RTL for 20-step CORDIC rotation
// Derived from given C reference
// =============================================================

module cordic (
    input  wire                  clk,
    input  wire                  rst_n,
    input  wire                  start,
    input  wire signed [32-1:0] target,
    output reg  signed [32-1:0] rX,
    output reg  signed [32-1:0] rY,
    output reg                   done
);

     localparam WIDTH = 32 
    // ---------------------------------------------------------
    // Constants
    // ---------------------------------------------------------
    localparam signed [WIDTH-1:0] AG_CONST = 32'd163008218;

    // Angles table (20 entries)
    localparam signed [WIDTH-1:0] angles [0:19] = '{
        32'd210828714, 32'd124459457, 32'd65760959, 32'd33381289,
        32'd16755421, 32'd8385878,  32'd4193962,  32'd2097109,
        32'd1048570,  32'd524287,   32'd262143,   32'd131071,
        32'd65535,    32'd32767,    32'd16383,    32'd8191,
        32'd4095,     32'd2047,     32'd1024,     32'd511
    };

    // ---------------------------------------------------------
    // Internal registers
    // ---------------------------------------------------------
    reg signed [WIDTH-1:0] X, Y, T, current;
    reg [4:0] step;
    reg running;

    // ---------------------------------------------------------
    // Sequential CORDIC process
    // ---------------------------------------------------------
    always @(posedge clk) begin
        if (!rst_n) begin
            X <= 0;
            Y <= 0;
            T <= 0;
            current <= 0;
            step <= 0;
            running <= 0;
            done <= 0;
            rX <= 0;
            rY <= 0;
        end else begin
            done <= 0;

            if (start && !running) begin
                // Initialize CORDIC iteration
                running <= 1;
                step <= 0;
                X <= AG_CONST;
                Y <= 0;
                current <= 0;
            end else if (running) begin
                // Perform one iteration per clock
                if (target > current) begin
                    X /*T*/ <= X - (Y >>> step);
                    Y <= (X >>> step) + Y;
                    /*X <= T;*/
                    current <= current + angles[step];
                end else begin
                    X /*T*/ <= X + (Y >>> step);
                    Y <= Y - (X >>> step);
                    /*X <= T;*/
                    current <= current - angles[step];
                end

                // Advance step
                if (step == 5'd19) begin
                    // Finish
                    running <= 0;
                    done <= 1;
                    rX <= X;
                    rY <= Y;
                end else begin
                    step <= step + 1;
                end
            end
        end
    end

endmodule // cordic

Here is a testbench that I also patched from CHATGPT

module cordic_tb;

`ifdef PERIOD
   localparam half_period=(`PERIOD+1)/2;
`else
   localparam half_period=50000;   
`endif

    // ---------------------------------------------------------
    // Parameters and DUT I/O
    // ---------------------------------------------------------
    localparam WIDTH = 32;
    reg                    clk;
    reg                    rst_n;
    reg                    start;
    reg  signed [WIDTH-1:0] target;
    wire signed [WIDTH-1:0] rX;
    wire signed [WIDTH-1:0] rY;
    wire                   done;

    // ---------------------------------------------------------
    // Instantiate DUT
    // ---------------------------------------------------------
    //cordic #(.WIDTH(WIDTH)) dut (
    cordic  dut (
        .clk(clk),
        .rst_n(rst_n),
        .start(start),
        .target(target),
        .rX(rX),
        .rY(rY),
        .done(done)
    );

    // ---------------------------------------------------------
    // Clock generation
    // ---------------------------------------------------------
    initial clk = 0;
    always #half_period clk = ~clk;  // 100 MHz clock (10 ns period)

    // ---------------------------------------------------------
    // Test sequence
    // ---------------------------------------------------------
    integer i;
    reg signed [WIDTH-1:0] test_targets [0:3];

    initial begin

       $dumpfile("waveform.vcd"); // Specifies the VCD file name
       $dumpvars(0, dut);

        // Preload test target angles (in same units as C code)
        test_targets[0] =  32'd210828714;   // small positive
        test_targets[1] = -32'd124459457;   // small negative
        test_targets[2] =  32'd65760959;    // smaller positive
        test_targets[3] =  32'd33381289;    // even smaller

        // Initialize signals
        rst_n <= 0;
        start <= 0;
        target <= 0;

        // Apply reset
        rst_n <= 1;
       @(posedge clk);
       @(posedge clk);
        rst_n <= 0;
       @(posedge clk);
       @(posedge clk);
        rst_n <= 1;
       @(posedge clk);
       @(posedge clk);


        // -----------------------------------------------------
        // Run through test cases
        // -----------------------------------------------------
        for (i = 0; i < 1; i = i + 1) begin
            target = test_targets[i];
            @(posedge clk);
            start <= 1;
            @(posedge clk);
            start <= 0;

            // Wait for done
            //wait(done);
            while(!done) begin 
               @(posedge clk);
               $strobe("%010t, done = %b, X=%x,Y=%x,rX=%x,rY=%x", $time,done,dut.X,dut.Y,rX,rY);
            end

            $strobe("TEST %0d: target = %0d => rX = %0d, rY = %0d", 
                      i, target, rX, rY);
            $strobe("TEST %0d: target = %0x => rX = %0x, rY = %0x", 
                      i, target, rX, rY);
        end
       @(posedge clk);
        $display("All tests complete.");
        $finish;
    end

endmodule // tb_cordic

Your assignment is to create new implementations and report timing slack and utilization and demonstrate operation

Using Google Gemini Notebook create the following:

  • cordic_registered_output.sv : a combinational registered-output version the computes the results entirely in one clock cycle and only has registers for the final outputs
  • cordic_pipelined.sv : a piplelined version, the unlike the reference version can accept new inputs as the previous results are being calculated

For each of these two versions and the previous (three versions)

  • perform Static Timing Analysis using a 100MHz design constraint
  • Based on the worst slack reported, estimate the maximum clock frequency for operation.

For all three versions

  • Generate a utilization report and summarize key the trade offs of each implementation for function, performance, and cost

Starter Scripts

Here is a generic template constraints file to support Out-of-Context Synthesis:

################################################################################
# Out-of-Context (OOC) Constraints File Generic
# Primary Clock Port: clk
################################################################################

# 1. Primary Clock Definition (100 MHz target -> 10.0 ns period, 50% duty cycle)
create_clock -name clk -period 10.000 -waveform {0.000 5.000} [get_ports clk]

# Assign a default global clock buffer site to enable clock insertion delay estimation
set_property HD.CLK_SRC BUFGCTRL_X0Y0 [get_ports clk]

# 2. Input / Output Delays for Standalone Boundary Timing Analysis
set_input_delay -clock clk 2.000 [get_ports -filter {DIRECTION == "IN" && NAME != "clk"} *]
set_output_delay -clock clk 2.000 [all_outputs]

The following is a starter script designed to be run from $PROJECT/build with source files appropriately in $PROJECT/src/tcl, $PROJECT/src/contraints, $PROJECT/src/scripts using the following command: /umbc/software/scripts/launch_xilinx_vivado.sh -mode tcl -source ../src/scripts/cordic_registered_ooc_timing.tcl

# ==============================================================================
# Vivado Tcl Script for Out-of-Context (OOC) Synthesis & Timing Analysis
# Module: cordic (cordic.sv)
# Target Device: Nexys A7-100T (Artix-7 xc7a100tcsg324-1)
# ==============================================================================

# 1. Setup Target Part and Output Directory
set TOP_MODULE "cordic_piplelined"
set PART_NAME "xc7a100tcsg324-1"
set OUTPUT_DIR "../build/cordic_pipelined_ooc"
set sourceDir "../src"
set XDC_FILE_PATH "../src/constraints/generic_ooc.xdc"

set_part $PART_NAME

file mkdir $OUTPUT_DIR

puts "------------------------------------------------------------------------"
puts "Starting Out-of-Context Synthesis for module: $TOP_MODULE"
puts "Target Part: $PART_NAME"
puts "------------------------------------------------------------------------"

# 2. Read SystemVerilog RTL Source
read_verilog -sv  $sourceDir/rtl/cordic_pipelined.sv

# 3. Read Out-of-Context Constraint File
read_xdc -mode out_of_context $XDC_FILE_PATH

# 4. Run Out-of-Context Synthesis
synth_design -top $TOP_MODULE -part $PART_NAME -mode out_of_context

# 5. Save Synthesized Design Checkpoint (DCP)
write_checkpoint -force $OUTPUT_DIR/${TOP_MODULE}_ooc.dcp

# 6. Generate Timing Analysis and Utilization Reports
report_timing_summary -delay_type min_max \
                      -report_unconstrained \
                      -check_timing_verbose \
                      -max_paths 10 \
                      -input_pins \
                      -file $OUTPUT_DIR/${TOP_MODULE}_timing_summary.rpt

report_utilization -file $OUTPUT_DIR/${TOP_MODULE}_utilization.rpt

puts "------------------------------------------------------------------------"
puts "OOC Synthesis Completed Successfully!"
puts "Reports generated in: $OUTPUT_DIR"
puts "------------------------------------------------------------------------"

P2 Physical Implementation of CORDIC reference version

(A) Skeleton Provided (get this working first)

This code includes python code to send and receive serial data, and a design that displays bytes transferred on LEDs and incomming words on the 7-segment array.
The next change that I might make is to use board switches as control to choose what is displayed on the seven segment LEDs, right now only the incomming 32-bit word is displayed.
Some of this was generated with the help of Gemini Notebook

Submission requirement: Hand-draw FSM diagram that documents the controller FSM implemented in this reference design and include it in your assignment submission.

(B) Integrate the reference CORDIC provided and demonstrate operation

  • Integrate the provided CORDIC implementation into the skeleton.
  • Document changes as hand-drawing for the controller FSM and implement those changes in the RTL.
  • Modify the Python Test Code (it would be very useful to have this perform results verification, not just operation)
  • Demonstrate Operation (share video with TA or live demonstration)
  • Submit all source files and brief report

P3 (Based on Tuesday Oct 27th Lecture)

For this problem the UMBC Red Light policy for AI must be adhered to: (https://my3.my.umbc.edu/groups/provost/posts/155412/2/57c54c0c9cd5cd90136fccec0cc38d27/web/link?link=https://my3.my.umbc.edu/groups/provost/files/14081)

AI Use Not Permitted
All work must be entirely your own. The use of generative AI tools is not allowed
at any stage of the assignment, including brainstorming, experiments, coding,
research, writing, or revision. If you have any questions, feel free to reach out to
me. The use of AI tools will be treated as an academic integrity issue.

All work is expected to be handwritten with steps shown and performed with use only of basic calculator functions as would be practiced for an exam: