mirror of
https://github.com/jhshi/openofdm.git
synced 2025-04-22 17:51:17 +00:00
readme
This commit is contained in:
parent
d3ff9e7ce8
commit
1ad9302fc3
28
Readme.rst
Normal file
28
Readme.rst
Normal file
@ -0,0 +1,28 @@
|
||||
OpenOFDM
|
||||
========
|
||||
|
||||
This project contains a Verilog implementation of 802.11 OFDM PHY decoder.
|
||||
Features are:
|
||||
|
||||
- Fully synthesizable (tested on Ettus Research USRP N210 platform)
|
||||
- Full support for legacy 802.11a/g
|
||||
- Support 802.11n for MCS 0 - 7 @ 20 MHz bandwidth
|
||||
- Cross validation with included Python decoder
|
||||
|
||||
|
||||
Environment Setup
|
||||
-----------------
|
||||
|
||||
This project has the following dependencies:
|
||||
|
||||
- `Icarus Verilog <http://iverilog.icarus.com/>`_: for compiling Verilog files and simulation.
|
||||
- `GtkWave <http://iverilog.icarus.com/>`_: for wave form visualization.
|
||||
|
||||
|
||||
Input and Output
|
||||
----------------
|
||||
|
||||
In a nutshell, the top level ``dot11`` Verilog module takes 32-bit I/Q samples
|
||||
(16-bit each) as input, and output decoded bytes in 802.11 packet. The sampling
|
||||
rate is 20 MSPS and the clock rate is 100 MHz. This means this module expects
|
||||
one pair of I/Q sample every 5 clock ticks.
|
@ -1,267 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import jsonschema
|
||||
import json
|
||||
import itertools
|
||||
|
||||
MCS_MAX = 7
|
||||
LENGTH_MAX = 4095
|
||||
|
||||
MAX_ACTION_LEN = 256
|
||||
|
||||
HEADER_LEN = 16
|
||||
|
||||
SR_RATE_FILTER = 7
|
||||
SR_LEN_FILTER = 8
|
||||
SR_HEADER_FILTER = 9
|
||||
SR_HEADER_LEN =10
|
||||
SR_JAM_POLICY = 11
|
||||
|
||||
POLICY_JSON_SCHEMA = """
|
||||
{
|
||||
"title": "Jamming Policy Format Specification",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/policy"
|
||||
},
|
||||
"minItems": 1,
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"policy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"length": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4095
|
||||
},
|
||||
"length_min": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4095
|
||||
},
|
||||
"length_max": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4095
|
||||
},
|
||||
|
||||
"mcs": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
"mcs_min": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
"mcs_max": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
|
||||
"header": {
|
||||
"type": "string",
|
||||
"maxLength": 47
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["Jam", "Pass", "JamAck"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-04/schema#"
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
def arg_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('file', help="JSON file that contains the policy")
|
||||
parser.add_argument('--out', help="Output file")
|
||||
return parser
|
||||
|
||||
|
||||
def check_policy(args):
|
||||
for policy in args.policies:
|
||||
if 'length' in policy and ('length_min' in policy or
|
||||
'length_max' in policy):
|
||||
print "[ERROR] Policy can not contain both exact and range "\
|
||||
"quantifiers for length"
|
||||
return False
|
||||
|
||||
if 'mcs' in policy and ('mcs_min' in policy or 'mcs_max' in policy):
|
||||
print "[ERROR] Policy can not contain both exact and range "\
|
||||
"quantifiers for mcs"
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def make_canonical(args):
|
||||
for policy in args.policies:
|
||||
has_mcs = any([k.startswith('mcs') for k in policy])
|
||||
has_length = any([k.startswith('length') for k in policy])
|
||||
has_header = 'header' in policy
|
||||
|
||||
if has_mcs:
|
||||
if 'mcs' in policy:
|
||||
policy['mcs_min'] = policy['mcs']
|
||||
policy['mcs_max'] = policy['mcs']
|
||||
del policy['mcs']
|
||||
|
||||
if 'mcs_min' not in policy:
|
||||
policy['mcs_min'] = 0
|
||||
|
||||
if 'mcs_max' not in policy:
|
||||
policy['mcs_max'] = MCS_MAX
|
||||
|
||||
policy['mcs_weight'] = 3
|
||||
if has_length:
|
||||
policy['mcs_weight'] -= 1
|
||||
if has_header:
|
||||
policy['mcs_weight'] -= 1
|
||||
else:
|
||||
policy['mcs_min'] = MCS_MAX
|
||||
policy['mcs_max'] = 0
|
||||
policy['mcs_weight'] = 0
|
||||
|
||||
if has_length:
|
||||
if 'length' in policy:
|
||||
policy['length_min'] = policy['length']
|
||||
policy['length_max'] = policy['length']
|
||||
del policy['length']
|
||||
|
||||
if 'length_min' not in policy:
|
||||
policy['length_min'] = 0
|
||||
|
||||
if 'length_max' not in policy:
|
||||
policy['length_max'] = LENGTH_MAX
|
||||
|
||||
policy['length_weight'] = 3 - policy['mcs_weight']
|
||||
if has_header:
|
||||
policy['mcs_weight'] -= 1
|
||||
else:
|
||||
policy['length_min'] = LENGTH_MAX
|
||||
policy['length_max'] = 0
|
||||
policy['length_weight'] = 0
|
||||
|
||||
if has_header:
|
||||
bytes = []
|
||||
for s in policy['header'].split():
|
||||
if s == 'xx':
|
||||
bytes.append(0x1ff)
|
||||
else:
|
||||
bytes.append(int(s, 16))
|
||||
policy['header'] = bytes
|
||||
else:
|
||||
policy['header'] = []
|
||||
|
||||
iter = itertools.cycle(policy['actions'])
|
||||
for _ in range(MAX_ACTION_LEN-len(policy['actions'])):
|
||||
policy['actions'].append(iter.next())
|
||||
|
||||
args.max_header_len = max([len(p['header']) for p in args.policies])
|
||||
for policy in args.policies:
|
||||
for _ in range(args.max_header_len - len(policy['header'])):
|
||||
policy['header'].append(0x1ff)
|
||||
|
||||
cano_file = '_canonical'.join(os.path.splitext(args.file))
|
||||
with open(cano_file, 'w') as f:
|
||||
f.write(json.dumps(args.policies, indent=2))
|
||||
|
||||
|
||||
def translate(args):
|
||||
rate_data = []
|
||||
len_data = []
|
||||
header_data = []
|
||||
action_data = []
|
||||
|
||||
for id, policy in enumerate(args.policies):
|
||||
val = (id<<28) + (policy['mcs_min']<<6) + (policy['mcs_max']<<2) +\
|
||||
(policy['mcs_weight']&0x3)
|
||||
rate_data.append(val)
|
||||
|
||||
val = (id<<28) + (policy['length_min']<<14) +\
|
||||
(policy['length_max']<<2) + (policy['length_weight']&0x3)
|
||||
len_data.append(val)
|
||||
|
||||
for addr, b in enumerate(policy['header']):
|
||||
val = (id<<28) + (addr<<23) + (b&0x1ff)
|
||||
header_data.append(val)
|
||||
|
||||
for addr, a in enumerate(policy['actions']):
|
||||
val = (id<<28) + (addr<<20)
|
||||
if a == 'Jam':
|
||||
val += 1
|
||||
elif a == 'Pass':
|
||||
val += 0
|
||||
elif a == 'JamAck':
|
||||
val += 2
|
||||
action_data.append(val)
|
||||
|
||||
with open(args.out, 'w') as f:
|
||||
f.write('0x%x\n' % (SR_RATE_FILTER))
|
||||
f.write('0x%x\n' % (len(rate_data)))
|
||||
for d in rate_data:
|
||||
f.write('0x%08x\n' % (d))
|
||||
|
||||
f.write('0x%x\n' % (SR_LEN_FILTER))
|
||||
f.write('0x%x\n' % (len(len_data)))
|
||||
for d in len_data:
|
||||
f.write('0x%08x\n' % (d))
|
||||
|
||||
f.write('0x%x\n' % (SR_HEADER_FILTER))
|
||||
f.write('0x%x\n' % (len(header_data)))
|
||||
for d in header_data:
|
||||
f.write('0x%08x\n' % (d))
|
||||
|
||||
f.write('0x%x\n' % (SR_HEADER_LEN))
|
||||
f.write('0x1\n')
|
||||
f.write('0x%x\n' % (args.max_header_len))
|
||||
|
||||
f.write('0x%x\n' % (SR_JAM_POLICY))
|
||||
f.write('0x%x\n' % (len(action_data)))
|
||||
for d in action_data:
|
||||
f.write('0x%08x\n' % (d))
|
||||
|
||||
print "Jam file written to %s" % (args.out)
|
||||
|
||||
|
||||
def main():
|
||||
args = arg_parser().parse_args()
|
||||
|
||||
if args.out is None:
|
||||
args.out = "%s.jam" % (os.path.splitext(args.file)[0])
|
||||
print "No output file specified, using %s" % (args.out)
|
||||
|
||||
schema = json.loads(POLICY_JSON_SCHEMA)
|
||||
|
||||
with open(args.file, 'r') as f:
|
||||
args.policies = json.loads(f.read())
|
||||
|
||||
print "Validating policy ..."
|
||||
jsonschema.validate(args.policies, schema)
|
||||
|
||||
if not check_policy(args):
|
||||
return
|
||||
|
||||
print "Making canonical policy..."
|
||||
make_canonical(args)
|
||||
|
||||
translate(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,63 +0,0 @@
|
||||
{
|
||||
"title": "Jamming Policy Format Specification",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/policy"
|
||||
},
|
||||
"minItems": 1,
|
||||
"additionalProperties": false,
|
||||
"definitions": {
|
||||
"policy": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"length": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4095
|
||||
},
|
||||
"length_min": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4095
|
||||
},
|
||||
"length_max": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 4095
|
||||
},
|
||||
|
||||
"mcs": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
"mcs_min": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
"mcs_max": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 7
|
||||
},
|
||||
|
||||
"header": {
|
||||
"type": "string",
|
||||
"maxLength": 47
|
||||
},
|
||||
"actions": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 256,
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["Jam", "Pass", "JamAck"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"$schema": "http://json-schema.org/draft-04/schema#"
|
||||
}
|
@ -1,14 +1,3 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// JAM FILTER STATUS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
localparam FILTER_NO_MATCH = 0;
|
||||
localparam FILTER_MATCH_PASS = 1;
|
||||
localparam FILTER_MATCH_JAM = 2;
|
||||
localparam FILTER_MATCH_JAM_ACK = 3;
|
||||
localparam FILTER_RATIO_PASS = 4;
|
||||
localparam FILTER_RATIO_JAM = 5;
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// PI DEFINITION
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -24,9 +13,6 @@ localparam PI_3_4 = PI_2 + PI_4;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// USER REG DEFINITION
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
localparam SR_JAMMER_ENABLE = 1;
|
||||
localparam SR_JAMMER_RESET = 2;
|
||||
|
||||
// power trigger
|
||||
localparam SR_POWER_THRES = 3;
|
||||
localparam SR_POWER_WINDOW = 4;
|
||||
@ -35,15 +21,6 @@ localparam SR_SKIP_SAMPLE = 5;
|
||||
// sync short
|
||||
localparam SR_MIN_PLATEAU = 6;
|
||||
|
||||
// filter
|
||||
localparam SR_RATE_FILTER = 7;
|
||||
localparam SR_LEN_FILTER = 8;
|
||||
localparam SR_HEADER_FILTER = 9;
|
||||
localparam SR_HEADER_LEN = 10;
|
||||
localparam SR_JAM_POLICY = 11;
|
||||
|
||||
localparam SR_JAM_SIGNAL = 12;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// DOT11 STATE MACHINE
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -5,6 +5,7 @@ module dot11 (
|
||||
input enable,
|
||||
input reset,
|
||||
|
||||
// setting registers
|
||||
input set_stb,
|
||||
input [7:0] set_addr,
|
||||
input [31:0] set_data,
|
||||
|
@ -1,125 +0,0 @@
|
||||
module viterbi_tb;
|
||||
|
||||
reg clock;
|
||||
reg reset;
|
||||
reg enable;
|
||||
|
||||
localparam RAM_SIZE = 1<<25;
|
||||
reg encoded_data [0:RAM_SIZE-1];
|
||||
reg [31:0] encoded_data_addr;
|
||||
|
||||
reg decoded_data [0:RAM_SIZE-1];
|
||||
reg [31:0] decoded_data_addr;
|
||||
|
||||
wire expected = decoded_data[decoded_data_addr];
|
||||
|
||||
reg [31:0] input_count;
|
||||
|
||||
|
||||
localparam ENCODED_DATA_FILE = "./test_in/conv_encoded_data.txt";
|
||||
localparam DECODED_DATA_FILE = "./test_in/conv_decoded_data.txt";
|
||||
|
||||
reg clr;
|
||||
reg sym0;
|
||||
reg sym1;
|
||||
|
||||
reg [31:0] error_count;
|
||||
|
||||
wire [15:0] ber;
|
||||
wire ber_done;
|
||||
wire data_out;
|
||||
wire rdy;
|
||||
|
||||
viterbi_v7_0 viterbi_inst (
|
||||
.clk(clock),
|
||||
.ce(1),
|
||||
.sclr(clr),
|
||||
.data_in0(sym0),
|
||||
.data_in1(sym1),
|
||||
.rdy(rdy),
|
||||
.data_out(data_out)
|
||||
);
|
||||
|
||||
initial begin
|
||||
$dumpfile("xilinx_viterbi_tb.vcd");
|
||||
$dumpvars;
|
||||
|
||||
$display("Reading memory from %s ...", ENCODED_DATA_FILE);
|
||||
$readmemb(ENCODED_DATA_FILE, encoded_data);
|
||||
$readmemb(DECODED_DATA_FILE, decoded_data);
|
||||
$display("Done.");
|
||||
|
||||
clock = 0;
|
||||
reset = 1;
|
||||
enable = 0;
|
||||
clr = 1;
|
||||
|
||||
# 100 reset = 0;
|
||||
# 100 enable = 1;
|
||||
|
||||
# 20000 $finish;
|
||||
end
|
||||
|
||||
|
||||
always begin
|
||||
#1 clock = !clock;
|
||||
end
|
||||
|
||||
localparam S_INPUT = 0;
|
||||
localparam S_FLUSH = 1;
|
||||
|
||||
reg [3:0] state;
|
||||
|
||||
localparam BITS_TO_DECODE = 48*6+48;
|
||||
|
||||
|
||||
always @(posedge clock) begin
|
||||
if (reset) begin
|
||||
sym0 <= 0;
|
||||
sym1 <= 0;
|
||||
input_count <= 0;
|
||||
|
||||
error_count <= 0;
|
||||
|
||||
encoded_data_addr <= 0;
|
||||
decoded_data_addr <= 0;
|
||||
state <= S_INPUT;
|
||||
end else if (enable) begin
|
||||
clr <= 0;
|
||||
case(state)
|
||||
S_INPUT: begin
|
||||
if (input_count < BITS_TO_DECODE) begin
|
||||
sym0 <= encoded_data[encoded_data_addr];
|
||||
sym1 <= encoded_data[encoded_data_addr+1];
|
||||
encoded_data_addr <= encoded_data_addr + 2;
|
||||
input_count <= input_count + 2;
|
||||
end else begin
|
||||
sym0 <= 0;
|
||||
sym1 <= 0;
|
||||
state <= S_FLUSH;
|
||||
end
|
||||
end
|
||||
|
||||
S_FLUSH: begin
|
||||
end
|
||||
endcase
|
||||
|
||||
if (rdy) begin
|
||||
$display("%d\t%d\t%d\t%d", decoded_data_addr, expected, data_out, error_count);
|
||||
if (data_out != expected) begin
|
||||
error_count <= error_count + 1;
|
||||
if (error_count > 500) begin
|
||||
$display("too many errors.");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
if (decoded_data_addr >= BITS_TO_DECODE/2) begin
|
||||
$finish;
|
||||
end else begin
|
||||
decoded_data_addr <= decoded_data_addr + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
Loading…
x
Reference in New Issue
Block a user