Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8ed0c66
Modify multiplier testbench to disable Q_BITWIDTH
AHseifadini Sep 18, 2026
641b962
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Sep 19, 2026
994909f
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Sep 21, 2026
cefbb44
Add PE matrix multiplication testbench
AHseifadini Sep 23, 2026
db3a160
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Sep 23, 2026
d36e955
Verify parallel PE computation for first input row
AHseifadini Sep 24, 2026
f3591bb
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Sep 24, 2026
6f49a57
Add Description
AHseifadini Sep 24, 2026
be51295
Fix PE cluster waveform generation
AHseifadini Sep 27, 2026
f22a16f
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Sep 27, 2026
28eeeb0
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Sep 28, 2026
9da588f
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Oct 1, 2026
ded5416
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Oct 3, 2026
7f6d6d7
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Oct 13, 2026
0065e50
Merge branch 'Learning-Chips-Lab:main' into Self-Attention
AHseifadini Oct 17, 2026
3026e3a
feat(attention): implement and verify GEMV controller FSM
AHseifadini Oct 17, 2026
747c4ed
feat(attention): add and unit-test basic DMA stream adapter
AHseifadini Oct 17, 2026
445463a
Update gitignore for simulation files
AHseifadini Oct 17, 2026
0d4f237
test(attention): verify controller and DMA adapter integration
AHseifadini Oct 18, 2026
781dafc
Fix the extra comma problem in generator.py
AHseifadini Oct 18, 2026
33e5277
test(attention): add and verify GEMV reference ROM
AHseifadini Oct 18, 2026
21a09c0
feat(attention): integrate and verify ROM-based GEMV execution on Ope…
AHseifadini Oct 19, 2026
556d059
feat(attention): add selectable ROM banks for GEMV execution
AHseifadini Oct 20, 2026
b0cebc0
Update gitignore
AHseifadini Oct 20, 2026
15a0c48
test(attention): verify consecutive Q0 and Q1 GEMVs without reset
AHseifadini Oct 20, 2026
a134068
chore: ignore generated Cocotb simulation directories
AHseifadini Oct 20, 2026
9c867b9
Attention: generate QKV projection streams and compact ROM/RAM images
AHseifadini Oct 22, 2026
9b24609
Attention: implement ROM RAM override with cocotb verification
AHseifadini Oct 22, 2026
2bea9ab
Attention : Add GEMV projection integration with ROM/RAM-based DMA st…
AHseifadini Oct 23, 2026
658e837
Switch attention GEMV projection to Dense mode
AHseifadini Oct 23, 2026
9cdeda3
Attention : Add and verify Q/K/V Buffer Integration
AHseifadini Oct 24, 2026
6398beb
Add Q/K quantization controller and verification tests
AHseifadini Oct 25, 2026
7ce3401
Integrate Q/K hardware quantization with OpenEye projection
AHseifadini Oct 25, 2026
082cbc5
test(attention): verify single QKT GEMV bit-exact on OpenEye RTL
AHseifadini Oct 26, 2026
cefde21
Attention: implement QKT operand packers and DMA memory
AHseifadini Oct 26, 2026
6df7b9c
Attention: integrate and verify QK^T computation with OpenEye + score…
AHseifadini Oct 27, 2026
d55e4d6
Attention: complete end-to-end QKT integration
AHseifadini Oct 27, 2026
6139ef8
Attention: extract global scheduler into standalone RTL module
AHseifadini Oct 27, 2026
90fd201
Attention: archive obsolete RTL and legacy tests
AHseifadini Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 117 additions & 0 deletions attention_rom_override_split.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""Split two verified OpenEye GEMV streams into ROM/RAM images.

Phase-1 fixed-geometry baseline for the Attention ROM-override architecture.
The script changes neither OpenEye RTL nor the original reference files.

Run at the OpenEye repository root:
python attention_rom_override_split.py

Defaults expect:
test/cocotb_attention_qkt/data/gemv_reference.mem
test/cocotb_attention_qkt/data/gemv_q1_reference.mem
"""

from __future__ import annotations

import argparse
import json
from pathlib import Path

WORD_BITS = 64
EXPECTED_WORDS = 939
IACT_START, IACT_END = 19, 27 # [19, 26]: 8 DMA words
WGHT_START, WGHT_END = 27, 267 # [27, 266]: 240 DMA words


def read_mem(path: Path) -> list[int]:
if not path.is_file():
raise FileNotFoundError(f"Missing reference stream: {path}")
words = []
for line_num, raw in enumerate(path.read_text(encoding="utf-8-sig").splitlines(), start=1):
line = raw.split("//", 1)[0].split("#", 1)[0].strip()
if not line:
continue
if line.startswith("0x"):
line = line[2:]
try:
value = int(line.replace("_", ""), 16)
except ValueError as exc:
raise ValueError(f"Invalid hex at {path}:{line_num}: {line!r}") from exc
if not 0 <= value < (1 << WORD_BITS):
raise ValueError(f"Word outside {WORD_BITS}-bit range at {path}:{line_num}")
words.append(value)
if len(words) != EXPECTED_WORDS:
raise ValueError(f"Expected {EXPECTED_WORDS} words in {path}; got {len(words)}")
return words


def write_mem(path: Path, words: list[int]) -> None:
path.write_text("".join(f"{value:016x}\n" for value in words), encoding="ascii")


def assemble(template: list[int], activations: list[int], weights: list[int]) -> list[int]:
if len(activations) != IACT_END - IACT_START:
raise ValueError("Unexpected activation RAM size")
if len(weights) != WGHT_END - WGHT_START:
raise ValueError("Unexpected weight RAM size")
result = template.copy()
result[IACT_START:IACT_END] = activations
result[WGHT_START:WGHT_END] = weights
return result


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--data-dir", type=Path, default=Path("test/cocotb_attention_qkt/data"))
parser.add_argument("--out-dir", type=Path, default=None)
args = parser.parse_args()
source_dir = args.data_dir
output_dir = args.out_dir or source_dir / "rom_override_baseline"

q0 = read_mem(source_dir / "gemv_reference.mem")
q1 = read_mem(source_dir / "gemv_q1_reference.mem")

differences = [i for i, (left, right) in enumerate(zip(q0, q1)) if left != right]
outside_iact = [i for i in differences if not (IACT_START <= i < IACT_END)]
if outside_iact:
raise ValueError(
f"Q0/Q1 differ outside the activation region at {outside_iact}. "
"Cannot share a single common template/weight image yet."
)

common_rom = q0.copy()
common_rom[IACT_START:WGHT_END] = [0] * (WGHT_END - IACT_START)
activation_q0 = q0[IACT_START:IACT_END]
activation_q1 = q1[IACT_START:IACT_END]
weight_wq = q0[WGHT_START:WGHT_END]

assert assemble(common_rom, activation_q0, weight_wq) == q0
assert assemble(common_rom, activation_q1, weight_wq) == q1

output_dir.mkdir(parents=True, exist_ok=True)
write_mem(output_dir / "common_rom.mem", common_rom)
write_mem(output_dir / "activation_q0_ram.mem", activation_q0)
write_mem(output_dir / "activation_q1_ram.mem", activation_q1)
write_mem(output_dir / "weight_wq_ram.mem", weight_wq)

manifest = {
"dma_word_bits": WORD_BITS,
"complete_stream_words": EXPECTED_WORDS,
"common_rom_words": len(common_rom),
"activation_override": {"start": IACT_START, "word_count": len(activation_q0)},
"weight_override": {"start": WGHT_START, "word_count": len(weight_wq)},
"q0_q1_differing_word_addresses": differences,
"quantization": "Unchanged in the common ROM; supervisor reviewing optimization.",
"skip_flags": "Baseline assumes all skip flags disabled.",
"scope": "Verified Q0/Q1 only; dynamic Q/K/V mapping follows later.",
}
(output_dir / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")

print("PASS: Both Q0 and Q1 streams reconstructed exactly (939 / 939 words each).")
print(f"Differences between source streams: {differences}")
print(f"Saved 4 .mem files and manifest.json to: {output_dir.resolve()}")
print("Next: compare the generated images against the Attention Python mapper output.")


if __name__ == "__main__":
main()
171 changes: 171 additions & 0 deletions hdl/attention_dma_adapter.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
`timescale 1ns / 1ps

//==============================================================================
// Module: attention_dma_adapter
// Project: OpenEye - Single-Head Attention Accelerator
//==============================================================================
// Description:
// Handles one GEMV transaction through the OpenEye_FPGA DMA interface.
//
// This initial version:
// - Reads preformatted 64-bit words from an external word source.
// - Streams the words to OpenEye.
// - Captures result words and their addresses.
// - Reports completion after the final output word.
//
// DMA packet generation and tensor storage are implemented separately.
// Input streaming initially follows OpenEye's existing continuous-stream
// testbench protocol. Per-word backpressure requires integration testing.
//==============================================================================

// DMA Adapter <--> OpenEye_FPGA Connections:
//
// Adapter Output OpenEye Input
// ---------------------------------------------------------------
// dma_data_o -----------> data_dma_i
// dma_valid_o -----------> enable_dma_i
// dma_result_ready_o -----------> ready_dma_i
//
// Adapter Input OpenEye Output
// ---------------------------------------------------------------
// dma_ready_i <----------- ready_dma_o
// dma_result_data_i <----------- data_dma_o
// dma_result_valid_i <----------- enable_dma_o
// dma_result_last_i <----------- last_data_o
//
// Data Flow:
// 1. Adapter -> OpenEye:
// Sends configuration, input activations, weights, and other
// required data through the DMA input interface.
//
// 2. OpenEye -> Adapter:
// Returns computed partial sums (PSUMs) through the DMA
// output interface.

module attention_dma_adapter #(
parameter integer DMA_WIDTH = 64,
parameter integer ADDR_WIDTH = 16
)(
input wire clk_i,
input wire rst_ni,

// Main Attention controller
input wire gemv_start_i,
output reg gemv_done_o,
output wire busy_o,

// Input word source (future packet generator / buffer)
input wire [ADDR_WIDTH-1:0] stream_word_count_i,
output wire [ADDR_WIDTH-1:0] stream_addr_o,
input wire [DMA_WIDTH-1:0] stream_data_i,

// DMA input interface of OpenEye_FPGA
input wire dma_ready_i,
output wire [DMA_WIDTH-1:0] dma_data_o,
output wire dma_valid_o,

// DMA output interface of OpenEye_FPGA
input wire [DMA_WIDTH-1:0] dma_result_data_i,
input wire dma_result_valid_i,
input wire dma_result_last_i,
output wire dma_result_ready_o,

// Interface to future result buffer
output wire result_write_o,
output wire [DMA_WIDTH-1:0] result_data_o,
output wire [ADDR_WIDTH-1:0] result_addr_o
);

//==========================================================================
// FSM States
//==========================================================================

localparam [1:0] IDLE = 2'd0;
localparam [1:0] WAIT_READY = 2'd1;
localparam [1:0] SEND_STREAM = 2'd2;
localparam [1:0] WAIT_RESULT = 2'd3;

reg [1:0] state_reg;

reg [ADDR_WIDTH-1:0] stream_addr_reg;
reg [ADDR_WIDTH-1:0] stream_count_reg;
reg [ADDR_WIDTH-1:0] result_addr_reg;

//==========================================================================
// Combinational Output Logic
//==========================================================================

assign busy_o = (state_reg != IDLE);

assign stream_addr_o = stream_addr_reg;

assign dma_data_o = stream_data_i;
assign dma_valid_o = (state_reg == SEND_STREAM);

assign dma_result_ready_o = (state_reg == WAIT_RESULT);

assign result_write_o = dma_result_valid_i && dma_result_ready_o;
assign result_data_o = dma_result_data_i;
assign result_addr_o = result_addr_reg;

//==========================================================================
// Main FSM
//==========================================================================

always @(posedge clk_i or negedge rst_ni) begin
if (!rst_ni) begin
state_reg <= IDLE;
stream_addr_reg <= 0;
stream_count_reg <= 0;
result_addr_reg <= 0;
gemv_done_o <= 1'b0;
end
else begin
gemv_done_o <= 1'b0;

case (state_reg)

IDLE: begin
if (gemv_start_i && stream_word_count_i != 0) begin
stream_count_reg <= stream_word_count_i;
stream_addr_reg <= 0;
result_addr_reg <= 0;
state_reg <= WAIT_READY;
end
end

WAIT_READY: begin
if (dma_ready_i) begin
state_reg <= SEND_STREAM;
end
end

SEND_STREAM: begin
if (stream_addr_reg == stream_count_reg - 1'b1) begin
state_reg <= WAIT_RESULT;
end
else begin
stream_addr_reg <= stream_addr_reg + 1'b1;
end
end

WAIT_RESULT: begin
if (result_write_o) begin
result_addr_reg <= result_addr_reg + 1'b1;

if (dma_result_last_i) begin
gemv_done_o <= 1'b1;
state_reg <= IDLE;
end
end
end

default: begin
state_reg <= IDLE;
end

endcase
end
end

endmodule
103 changes: 103 additions & 0 deletions hdl/attention_gemv_control_top.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
`timescale 1ns / 1ps

//==============================================================================
// Module: attention_gemv_control_top
// Project: OpenEye - Single-Head Attention Accelerator
//==============================================================================
// Description:
// Integrates the Attention GEMV controller and DMA adapter.
//
// The controller manages execution requests, while the DMA adapter
// handles the transfer of input and output data.
//
// This module is intended for integration testing before connecting
// the actual OpenEye_FPGA accelerator.
//==============================================================================

module attention_gemv_control_top #(
parameter integer DMA_WIDTH = 64,
parameter integer ADDR_WIDTH = 16
)(
input wire clk_i,
input wire rst_ni,

// External operation control
input wire start_i,
output wire busy_o,
output wire done_o,
output wire adapter_busy_o,

// Input stream source
input wire [ADDR_WIDTH-1:0] stream_word_count_i,
output wire [ADDR_WIDTH-1:0] stream_addr_o,
input wire [DMA_WIDTH-1:0] stream_data_i,

// DMA input connection
input wire dma_ready_i,
output wire [DMA_WIDTH-1:0] dma_data_o,
output wire dma_valid_o,

// DMA result connection
input wire [DMA_WIDTH-1:0] dma_result_data_i,
input wire dma_result_valid_i,
input wire dma_result_last_i,
output wire dma_result_ready_o,

// Result storage interface
output wire result_write_o,
output wire [DMA_WIDTH-1:0] result_data_o,
output wire [ADDR_WIDTH-1:0] result_addr_o
);

// Internal connections between the two modules
wire gemv_start_wire;
wire gemv_done_wire;

//==========================================================================
// Attention Controller Instance
//==========================================================================

attention_qkt_ctrl u_attention_ctrl (
.clk_i (clk_i),
.rst_ni (rst_ni),
.start_i (start_i),
.gemv_done_i (gemv_done_wire),
.gemv_start_o (gemv_start_wire),
.busy_o (busy_o),
.done_o (done_o)
);

//==========================================================================
// DMA Adapter Instance
//==========================================================================

attention_dma_adapter #(
.DMA_WIDTH (DMA_WIDTH),
.ADDR_WIDTH (ADDR_WIDTH)
) u_dma_adapter (
.clk_i (clk_i),
.rst_ni (rst_ni),

.gemv_start_i (gemv_start_wire),
.gemv_done_o (gemv_done_wire),
.busy_o (adapter_busy_o),

.stream_word_count_i (stream_word_count_i),
.stream_addr_o (stream_addr_o),
.stream_data_i (stream_data_i),

.dma_ready_i (dma_ready_i),
.dma_data_o (dma_data_o),
.dma_valid_o (dma_valid_o),

.dma_result_data_i (dma_result_data_i),
.dma_result_valid_i (dma_result_valid_i),
.dma_result_last_i (dma_result_last_i),
.dma_result_ready_o (dma_result_ready_o),

.result_write_o (result_write_o),
.result_data_o (result_data_o),
.result_addr_o (result_addr_o)
);

endmodule
Loading
Loading