This commit is contained in:
Jinghao Shi 2017-04-03 14:31:25 -04:00
parent d3ff9e7ce8
commit 1ad9302fc3
6 changed files with 29 additions and 478 deletions

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