Download rtl/src/processing_element.sv from Snapkitty/flash-attention-rtl: direct link, hf CLI and curl.
- Browser
- Download file 5.06 kB
-
https://huggingface.co/Snapkitty/flash-attention-rtl/resolve/main/rtl/src/processing_element.sv
- Command line
-
hf download hf://Snapkitty/flash-attention-rtl/rtl/src/processing_element.sv
-
curl -L -o processing_element.sv https://huggingface.co/Snapkitty/flash-attention-rtl/resolve/main/rtl/src/processing_element.sv
5.06 kB
| /** | |
| * Processing Element (PE) for Systolic Array | |
| * | |
| * Implements a single multiply-accumulate (MAC) unit with pipeline registers. | |
| * | |
| * Functionality: | |
| * - Multiplies 8-bit input A and B (Q4.4 fixed-point) | |
| * - Accumulates result with incoming partial sum C_in | |
| * - Broadcasts A to right neighbor (matrix_A_out) | |
| * - Broadcasts C (accumulation) to bottom neighbor (matrix_C_out) | |
| * | |
| * Pipeline Stages: | |
| * Stage 1: Input capture (A, B, C_in, valid_in) | |
| * Stage 2: Multiply (8x8 -> 16-bit product) | |
| * Stage 3: Accumulate (16-bit + 16-bit -> 16-bit) | |
| * | |
| * Timing: 3-cycle latency from valid_in to valid_out | |
| */ | |
| module processing_element #( | |
| parameter DATA_WIDTH = 8, // Input data width (Q4.4 = 8-bit) | |
| parameter ACC_WIDTH = 16, // Accumulator width (to avoid overflow) | |
| parameter INIT_VALUE = 16'b0 // Initial accumulator value | |
| ) ( | |
| input logic clk, | |
| input logic rst_n, | |
| // Input port (from left and top) | |
| input logic valid_in, | |
| input logic [DATA_WIDTH-1:0] matrix_A_in, // Data from left (Q) | |
| input logic [DATA_WIDTH-1:0] matrix_B_in, // Data from top (K^T) | |
| input logic [ACC_WIDTH-1:0] matrix_C_in, // Partial sum from top | |
| // Output port (to right and bottom) | |
| output logic valid_out, | |
| output logic [DATA_WIDTH-1:0] matrix_A_out, // Forward A to right | |
| output logic [ACC_WIDTH-1:0] matrix_C_out // Forward sum to bottom | |
| ); | |
| // ===================================================================== | |
| // Stage 1: Input Registers (capture A, B, C_in on valid_in) | |
| // ===================================================================== | |
| logic valid_s1; | |
| logic [DATA_WIDTH-1:0] A_s1; | |
| logic [DATA_WIDTH-1:0] B_s1; | |
| logic [ACC_WIDTH-1:0] C_s1; | |
| always_ff @(posedge clk or negedge rst_n) begin | |
| if (!rst_n) begin | |
| valid_s1 <= 1'b0; | |
| A_s1 <= {DATA_WIDTH{1'b0}}; | |
| B_s1 <= {DATA_WIDTH{1'b0}}; | |
| C_s1 <= {ACC_WIDTH{1'b0}}; | |
| end else begin | |
| valid_s1 <= valid_in; | |
| A_s1 <= matrix_A_in; | |
| B_s1 <= matrix_B_in; | |
| C_s1 <= matrix_C_in; | |
| end | |
| end | |
| // ===================================================================== | |
| // Stage 2: Multiply (8-bit × 8-bit -> 16-bit) | |
| // ===================================================================== | |
| // Using signed multiplication for Q4.4 fixed-point | |
| // Result: 16-bit signed product | |
| logic valid_s2; | |
| logic [ACC_WIDTH-1:0] product_s2; // 8-bit × 8-bit = 16-bit | |
| logic [ACC_WIDTH-1:0] C_s2; | |
| always_ff @(posedge clk or negedge rst_n) begin | |
| if (!rst_n) begin | |
| valid_s2 <= 1'b0; | |
| product_s2 <= {ACC_WIDTH{1'b0}}; | |
| C_s2 <= {ACC_WIDTH{1'b0}}; | |
| end else begin | |
| valid_s2 <= valid_s1; | |
| // Signed multiplication: 8-bit × 8-bit -> 16-bit | |
| product_s2 <= $signed(A_s1) * $signed(B_s1); | |
| C_s2 <= C_s1; | |
| end | |
| end | |
| // ===================================================================== | |
| // Stage 3: Accumulate (product + C_s2 -> 16-bit result with saturation) | |
| // ===================================================================== | |
| logic valid_s3; | |
| logic [ACC_WIDTH-1:0] accumulator; | |
| // Saturation logic: prevent overflow in accumulation | |
| logic [ACC_WIDTH:0] sum_with_overflow; | |
| always_ff @(posedge clk or negedge rst_n) begin | |
| if (!rst_n) begin | |
| valid_s3 <= 1'b0; | |
| accumulator <= INIT_VALUE; | |
| end else begin | |
| valid_s3 <= valid_s2; | |
| // Add product to partial sum with overflow detection | |
| sum_with_overflow = $signed(product_s2) + $signed(C_s2); | |
| // Saturate to 16-bit range if overflow occurs | |
| if (sum_with_overflow[ACC_WIDTH] != sum_with_overflow[ACC_WIDTH-1]) begin | |
| // Overflow detected: saturate | |
| if (sum_with_overflow[ACC_WIDTH]) begin | |
| accumulator <= {1'b0, {(ACC_WIDTH-1){1'b1}}}; // Max positive | |
| end else begin | |
| accumulator <= {1'b1, {(ACC_WIDTH-1){1'b0}}}; // Min negative | |
| end | |
| end else begin | |
| // No overflow: store result | |
| accumulator <= sum_with_overflow[ACC_WIDTH-1:0]; | |
| end | |
| end | |
| end | |
| // ===================================================================== | |
| // Output Assignment | |
| // ===================================================================== | |
| assign valid_out = valid_s3; | |
| assign matrix_A_out = A_s1; // Forward A to right neighbor | |
| assign matrix_C_out = accumulator; // Forward accumulation to bottom neighbor | |
| endmodule : processing_element | |