commit 20db1eceb0d6fed70d904296e7d8992e2da424c5
parent 87ef6109809d9325d53d4f39cf8615f9cc537c11
Author: Brian Swetland <swetland@frotz.net>
Date: Sun, 27 Jul 2014 20:48:03 -0700
eth-capture and friends: capture inbound frames to axi
axi_dma_writer: commit 1-16 word bursts over axi
pkt_bytes_to_words: convert bytestream to wordstream
eth_capture: capture packets to a 4MB ring (currently at 0x10000000)
- packets are located at 4k offsets
- first 4 words of each packet are
- bytecount
- timestamp(low)
- timestamp(high)
- status(1=packet written)
- data starts at 64 bytes in to each packet
Diffstat:
A | hdl/axi_dma_writer.sv | | | 150 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | hdl/eth_capture.sv | | | 227 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | hdl/pkt_bytes_to_words.sv | | | 143 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 520 insertions(+), 0 deletions(-)
diff --git a/hdl/axi_dma_writer.sv b/hdl/axi_dma_writer.sv
@@ -0,0 +1,150 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+`timescale 1ns/1ps
+
+// on the clock where start=1
+// addr is the first address of the transfer
+// burstlen is the transfer wordcount minus 1
+// data is the first word of the transfer
+// on the clock where advance=1
+// data advances to the next word of the transfer
+// busy is asserted from the cycle after start until transfer complete
+
+module axi_dma_writer(
+ input clk,
+ axi_ifc.master m,
+ input start,
+ input [31:0]addr,
+ input [3:0]burstlen,
+ input [31:0]data,
+ output reg busy = 0,
+ output advance
+ );
+
+localparam STATE_IDLE = 4'd0;
+localparam STATE_WADDR = 4'd1;
+localparam STATE_WDATA = 4'd2;
+localparam STATE_ACK = 4'd3;
+
+initial m.awvalid = 0;
+initial m.wvalid = 0;
+
+reg [31:0]waddr = 0;
+reg [31:0]waddr_next;
+
+reg [3:0]state = STATE_IDLE;
+reg [3:0]state_next;
+
+reg awvalid_next;
+reg wvalid_next;
+reg bready_next;
+reg wlast_next;
+
+reg [3:0]count = 15;
+reg [3:0]count_next;
+
+reg busy_next;
+
+wire count_is_zero = (count == 4'h0);
+wire count_is_one = (count == 4'h1);
+
+assign advance = m.wready & busy;
+
+always_comb begin
+ state_next = state;
+ count_next = count;
+ waddr_next = waddr;
+ busy_next = busy;
+ awvalid_next = 0;
+ wvalid_next = 0;
+ bready_next = 0;
+ wlast_next = 0;
+
+ case (state)
+ STATE_IDLE: begin
+ if (start) begin
+ state_next = STATE_WADDR;
+ count_next = burstlen;
+ waddr_next = addr;
+ busy_next = 1;
+ awvalid_next = 1;
+ end
+ end
+ STATE_WADDR: begin
+ if (m.awready) begin
+ state_next = STATE_WDATA;
+ wvalid_next = 1;
+ wlast_next = count_is_zero;
+ end else begin
+ awvalid_next = 1;
+ end
+ end
+ STATE_WDATA: begin
+ if (count_is_zero) begin
+ if (m.wready) begin
+ state_next = STATE_ACK;
+ bready_next = 1;
+ end else begin
+ wvalid_next = 1;
+ wlast_next = 1;
+ end
+ end else begin
+ wvalid_next = 1;
+ if (m.wready) begin
+ if (count_is_one) begin
+ wlast_next = 1;
+ end
+ count_next = count - 1;
+ end
+ end
+ end
+ STATE_ACK: begin
+ if (m.bvalid) begin
+ state_next = STATE_IDLE;
+ busy_next = 0;
+ end else begin
+ bready_next = 1;
+ end
+ end
+ default: state_next = STATE_IDLE;
+ endcase
+end
+
+assign m.awid = 0;
+assign m.awburst = 1;
+assign m.awcache = 0;
+assign m.awsize = 2;
+assign m.awlen = count;
+assign m.awlock = 0;
+
+assign m.awaddr = waddr;
+assign m.wdata = data;
+assign m.wstrb = 4'b1111;
+
+always_ff @(posedge clk) begin
+ state <= state_next;
+ count <= count_next;
+ waddr <= waddr_next;
+ busy <= busy_next;
+ m.awvalid <= awvalid_next;
+ m.wvalid <= wvalid_next;
+ m.bready <= bready_next;
+ m.wlast <= wlast_next;
+end
+
+assign m.arvalid = 0;
+assign m.rvalid = 0;
+
+endmodule
diff --git a/hdl/eth_capture.sv b/hdl/eth_capture.sv
@@ -0,0 +1,227 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+`timescale 1ns / 1ps
+
+module eth_capture(
+ // interface from eth_rmii_rx
+ input clk50,
+ input rxsop,
+ input rxeop,
+ input [7:0]rxdata,
+ input rxvalid,
+
+ // interface to axi
+ input clk,
+ input reset,
+ axi_ifc axi_dma
+ );
+
+
+wire [31:0]w_data;
+wire w_valid;
+wire w_eop;
+wire [11:0]bytecount;
+
+reg [63:0]clk50_count = 0;
+reg [63:0]rxtimestamp = 0;
+always @(posedge clk50) begin
+ clk50_count <= clk50_count + 1;
+ if (rxsop) begin
+ rxtimestamp <= clk50_count;
+ end
+end
+
+pkt_bytes_to_words cap0(
+ .clk(clk50),
+ .rxdata(rxdata),
+ .rxvalid(rxvalid),
+ .rxeop(rxeop),
+ .data(w_data),
+ .bytecount(bytecount),
+ .eop(w_eop),
+ .valid(w_valid)
+ );
+
+typedef enum { DATA, EOP0, EOP1, EOP2 } state_t;
+state_t state = DATA;
+state_t next_state;
+
+reg [19:0]cfifo_in = 0;
+reg [31:0]dfifo_in = 0;
+reg [19:0]next_cfifo_in;
+reg [31:0]next_dfifo_in;
+reg cfifo_wr = 0;
+reg dfifo_wr = 0;
+reg next_cfifo_wr;
+reg next_dfifo_wr;
+reg [3:0]dcount = 15;
+reg [3:0]next_dcount;
+
+// ADDR: window(10) packet(10) chunk(6) off(6)
+reg [9:0]dbase = 0;
+reg [9:0]next_dbase;
+reg [5:0]daddr = 1;
+reg [5:0]next_daddr;
+
+wire [3:0]dcount_plus_one = (dcount + 1);
+
+always_comb begin
+ next_state = state;
+ next_dcount = dcount;
+ next_daddr = daddr;
+ next_dbase = dbase;
+ next_cfifo_in = cfifo_in;
+ next_dfifo_in = dfifo_in;
+ next_cfifo_wr = 0;
+ next_dfifo_wr = 0;
+ case (state)
+ DATA: begin
+ if (w_valid) begin
+ next_dcount = dcount_plus_one;
+ next_dfifo_in = w_data;
+ next_dfifo_wr = 1;
+ if (dcount_plus_one == 15) begin
+ next_cfifo_in = { 4'd15, dbase, daddr };
+ next_cfifo_wr = 1;
+ next_daddr = daddr + 1;
+ end
+ end else if (w_eop) begin
+ next_state = EOP0;
+ next_dfifo_in = { 20'h00000, bytecount };
+ next_dfifo_wr = 1;
+ if (dcount != 15) begin
+ next_cfifo_in = { dcount, dbase, daddr };
+ next_cfifo_wr = 1;
+ end
+ end
+ end
+ EOP0: begin
+ next_state = EOP1;
+ next_dfifo_in = rxtimestamp[31:0];
+ next_dfifo_wr = 1;
+ end
+ EOP1: begin
+ next_state = EOP2;
+ next_dfifo_in = rxtimestamp[63:32];
+ next_dfifo_wr = 1;
+ end
+ EOP2: begin
+ next_state = DATA;
+ next_dcount = 15;
+ next_daddr = 1;
+ next_dbase = dbase + 1;
+ next_dfifo_in = 32'h00000001; // status
+ next_dfifo_wr = 1;
+ next_cfifo_in = { 4'd3, dbase, 6'd0 };
+ next_cfifo_wr = 1;
+ end
+ endcase
+end
+
+always_ff @(posedge clk50) begin
+ state <= next_state;
+ dcount <= next_dcount;
+ daddr <= next_daddr;
+ dbase <= next_dbase;
+ cfifo_in <= next_cfifo_in;
+ dfifo_in <= next_dfifo_in;
+ cfifo_wr <= next_cfifo_wr;
+ dfifo_wr <= next_dfifo_wr;
+end
+
+reg fifo_reset = 0;
+
+wire [31:0]dfifo_data;
+reg dfifo_rd = 0;
+wire dfifo_empty;
+wire dfifo_active;
+
+wire [19:0]cfifo_data;
+reg cfifo_rd = 0;
+wire cfifo_empty;
+wire cfifo_active;
+
+// on reset request, assert fifo_reset until both fifos
+// have started the sequence (fifo_active=0)
+always @(posedge clk) begin
+ if (dfifo_active & cfifo_active) begin
+ if (reset) begin
+ fifo_reset <= 1;
+ end
+ end else if (~dfifo_active & ~cfifo_active) begin
+ fifo_reset <= 0;
+ end
+end
+
+// CMD: wrstatus wrdata count(12)
+xilinx_async_fifo #(
+ .WIDTH(32)
+ ) dfifo (
+ .wrclk(clk50),
+ .rdclk(clk),
+ .reset(fifo_reset),
+ .wr_data(dfifo_in),
+ .wr_en(dfifo_wr),
+ .rd_en(dfifo_rd),
+ .rd_data(dfifo_data),
+ .o_empty(dfifo_empty),
+ .o_ready(),
+ .o_active(dfifo_active)
+ );
+
+// CMD: burst(4) addr(16) -- addr is A[21:6]
+
+xilinx_async_fifo #(
+ .WIDTH(20)
+ ) cfifo (
+ .wrclk(clk50),
+ .rdclk(clk),
+ .reset(fifo_reset),
+ .wr_data(cfifo_in),
+ .wr_en(cfifo_wr),
+ .rd_en(cfifo_rd),
+ .rd_data(cfifo_data),
+ .o_empty(cfifo_empty),
+ .o_ready(),
+ .o_active(cfifo_active)
+ );
+
+reg [31:0]dma_base = 32'h10000000;
+
+wire [31:0]dma_addr = { dma_base[31:22], cfifo_data[15:0], 6'd0 };
+wire [3:0]dma_len = cfifo_data[19:16];
+wire dma_start = (~cfifo_empty) & (~dma_busy);
+wire dma_busy;
+
+always @(posedge clk) begin
+ if (dma_start) begin
+ cfifo_rd <= 1;
+ end else begin
+ cfifo_rd <= 0;
+ end
+end
+
+axi_dma_writer dma0(
+ .clk(clk),
+ .m(axi_dma),
+ .addr(dma_addr),
+ .burstlen(dma_len),
+ .start(dma_start),
+ .busy(dma_busy),
+ .data(dfifo_data),
+ .advance(dfifo_rd)
+ );
+
+endmodule
diff --git a/hdl/pkt_bytes_to_words.sv b/hdl/pkt_bytes_to_words.sv
@@ -0,0 +1,143 @@
+// Copyright 2014 Brian Swetland <swetland@frotz.net>
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+`timescale 1ns / 1ps
+
+
+// converts packet bytestream to wordstream
+//
+// input: rxdata accepted when rxvalid=1
+// rxeop terminates stream
+// rxeop may not happen on same clock as rxvalid
+// output: data and bytecount presented with valid=1
+// eop=1 indicates end of stream
+// eop and valid will not happen on the same clock
+
+module pkt_bytes_to_words(
+ input clk,
+ input [7:0]rxdata,
+ input rxvalid,
+ input rxeop,
+
+ output [31:0]data,
+ output [11:0]bytecount,
+ output reg valid = 0,
+ output reg eop = 0
+ );
+
+typedef enum { IDLE, BYTE0, BYTE1, BYTE2, BYTE3, EOP } state_t;
+state_t state = IDLE;
+state_t next_state;
+
+reg [11:0]count = 0;
+wire [11:0]count_plus_one = (count + 1);
+reg [11:0]next_count;
+reg [7:0]byte0 = 0;
+reg [7:0]next_byte0;
+reg [7:0]byte1 = 0;
+reg [7:0]next_byte1;
+reg [7:0]byte2 = 0;
+reg [7:0]next_byte2;
+reg [7:0]byte3 = 0;
+reg [7:0]next_byte3;
+
+reg next_valid;
+reg next_eop;
+
+assign data = { byte3, byte2, byte1, byte0 };
+assign bytecount = count;
+
+always_comb begin
+ next_state = state;
+ next_count = count;
+ next_byte0 = byte0;
+ next_byte1 = byte1;
+ next_byte2 = byte2;
+ next_byte3 = byte3;
+ next_valid = 0;
+ next_eop = 0;
+
+ case (state)
+ IDLE: begin
+ if (rxvalid) begin
+ next_state = BYTE0;
+ next_byte0 = rxdata;
+ next_byte1 = 0;
+ next_byte2 = 0;
+ next_byte3 = 0;
+ next_count = 1;
+ end
+ end
+ BYTE0: begin
+ if (rxvalid) begin
+ next_state = BYTE1;
+ next_byte1 = rxdata;
+ next_count = count_plus_one;
+ end else if (rxeop) begin
+ next_state = EOP;
+ next_valid = 1;
+ end
+ end
+ BYTE1: begin
+ if (rxvalid) begin
+ next_state = BYTE2;
+ next_byte2 = rxdata;
+ next_count = count_plus_one;
+ end else if (rxeop) begin
+ next_state = EOP;
+ next_valid = 1;
+ end
+ end
+ BYTE2: begin
+ if (rxvalid) begin
+ next_state = BYTE3;
+ next_byte3 = rxdata;
+ next_count = count_plus_one;
+ next_valid = 1;
+ end else if (rxeop) begin
+ next_state = EOP;
+ next_valid = 1;
+ end
+ end
+ BYTE3: begin
+ if (rxvalid) begin
+ next_state = BYTE0;
+ next_byte0 = rxdata;
+ next_byte1 = 0;
+ next_byte2 = 0;
+ next_byte3 = 0;
+ next_count = count_plus_one;
+ end else if (rxeop) begin
+ next_state = EOP;
+ end
+ end
+ EOP: begin
+ next_state = IDLE;
+ next_eop = 1;
+ end
+ endcase
+end
+
+always_ff @(posedge clk) begin
+ state <= next_state;
+ count <= next_count;
+ byte0 <= next_byte0;
+ byte1 <= next_byte1;
+ byte2 <= next_byte2;
+ byte3 <= next_byte3;
+ valid <= next_valid;
+ eop <= next_eop;
+end
+
+endmodule