How do you implement active-low reset with named ports? - chisel

I'm attempting to use Records along with 'withClockAndReset()' to create an interface Vivado will use, along with the expected negated reset.
The approach so far looks like:
// Borrowed from https://github.com/freechipsproject/chisel3/blob/master/src/test/scala/chiselTests/RecordSpec.scala
final class CustomBundle(elts: (String, Data)*) extends Record {
val elements = ListMap(elts map { case (field, elt) =>
requireIsChiselType(elt)
field -> elt
}: _*)
def apply(elt: String): Data = elements(elt)
override def cloneType: this.type = {
val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) }
(new CustomBundle(cloned: _*)).asInstanceOf[this.type]
}
}
class vivadoSlaveWrapper(regCount:Int, addrBits: Int, dataBits:Int) extends RawModule {
val io = IO(new CustomBundle(
"aclk" -> Input(Clock()),
"aresetn" -> Input(Bool()),
"s_axi_awaddr" -> Input(UInt(addrBits.W)),
"s_axi_awvalid" -> Input(Bool()),
"s_axi_awready" -> Output(Bool()),
"s_axi_awprot" -> Input(UInt(3.W)),
"s_axi_bresp" -> Output(UInt(2.W)),
"s_axi_bvalid" -> Output(Bool()),
"s_axi_bready" -> Input(Bool()),
"s_axi_wdata" -> Input(UInt(dataBits.W)),
"s_axi_wvalid" -> Input(Bool()),
"s_axi_wready" -> Output(Bool()),
"s_axi_wstrb" -> Input(UInt({dataBits/8}.W)),
"s_axi_araddr" -> Input(UInt(addrBits.W)),
"s_axi_arvalid" -> Input(Bool()),
"s_axi_arready" -> Output(Bool()),
"s_axi_arprot" -> Input(UInt(3.W)),
"s_axi_rdata" -> Output(UInt(dataBits.W)),
"s_axi_rvalid" -> Output(Bool()),
"s_axi_rready" -> Input(Bool()),
"s_axi_rresp" -> Output(UInt(2.W))
))
withClockAndReset(io("aclk"), ~io("aresetn")) {
val mod = Module( new simpleRegBank(regCount, addrBits, dataBits) )
mod.io.aw.addr := io("s_axi_awaddr" )
mod.io.aw.valid := io("s_axi_awvalid")
io("s_axi_awready") := mod.io.aw.ready
mod.io.aw.prot := io("s_axi_awprot" )
io("s_axi_bresp" ) := mod.io.b.bits
io("s_axi_bvalid" ) := mod.io.b.valid
mod.io.b.ready := io("s_axi_bready" )
mod.io.w.data := io("s_axi_wdata" )
mod.io.w.valid := io("s_axi_wvalid" )
io("s_axi_wready" ) := mod.io.w.ready
mod.io.w.strb := io("s_axi_wstrb" )
mod.io.ar.addr :=io("s_axi_araddr" )
mod.io.ar.valid := io("s_axi_arvalid")
io("s_axi_arready") := mod.io.ar.ready
mod.io.ar.prot := io("s_axi_arprot" )
io("s_axi_rdata" ) := mod.io.r.data
io("s_axi_rvalid" ) := mod.io.r.valid
mod.io.r.ready := io("s_axi_rready" )
io("s_axi_rresp" ) := mod.io.r.resp
}
}
but this results in errors around which types are being used in this case. I suspect this is mostly a Scala question, but I'm not quite sure how to resolve this to work for Clock and Reset.
The error is as below:
[error] /.../chisel/src/main/scala/axi4lite/axi4lite.scala:219:23: type mismatch;
[error] found : chisel3.Data
[error] (which expands to) chisel3.core.Data
[error] required: chisel3.core.Clock
[error] withClockAndReset(io("aclk"), io("aresetn")) {
[error] ^
[error] /.../chisel/src/main/scala/axi4lite/axi4lite.scala:219:35: type mismatch;
[error] found : chisel3.Data
[error] (which expands to) chisel3.core.Data
[error] required: chisel3.core.Reset
[error] withClockAndReset(io("aclk"), io("aresetn")) {
[error] ^
[error] two errors found
[error] (Compile / compileIncremental) Compilation failed

Related

ejabberd add_channel API for MIX

Currently, there is no api for creating a MIX channel.
I'm written a custom module for the same.
So far, I have written the following code. But I'm not how to proceed further.
I would really appreciate someone's guidance here. Thanks in advance.
-module(mod_custom).
-behaviour(gen_mod).
-include("logger.hrl").
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2]).
-export([
% Create channel
add_channel/4
]).
-include("ejabberd_commands.hrl").
-include("ejabberd_sm.hrl").
-include("xmpp.hrl").
start(_Host, _Opts) ->
ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_commands:unregister_commands(get_commands_spec());
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[].
get_commands_spec() ->
[
#ejabberd_commands{name = add_channel, tags = [group],
desc = "Create a WhatsApp like group",
module = ?MODULE, function = add_channel,
args = [{jid, binary}, {channel, binary}, {id, binary}],
args_example = [<<"admin#localhost">>, <<"testgroup123#localhost">>, <<"abc123456">>],
args_desc = ["Admin JID", "Channel JID", "Unique ID"],
result = {res, rescode}}
].
add_channel(JID, Channel, ID) ->
%%% Create channel code goes here...
ok.
mod_options(_) -> [].
Try something like this:
-module(mod_custom).
-behaviour(gen_mod).
-export([start/2, stop/1, reload/3, mod_options/1,
get_commands_spec/0, depends/2]).
-export([create_channel/3]).
-include("logger.hrl").
-include("ejabberd_commands.hrl").
-include("ejabberd_sm.hrl").
-include_lib("xmpp/include/xmpp.hrl").
start(_Host, _Opts) ->
ejabberd_commands:register_commands(get_commands_spec()).
stop(Host) ->
case gen_mod:is_loaded_elsewhere(Host, ?MODULE) of
false ->
ejabberd_commands:unregister_commands(get_commands_spec());
true ->
ok
end.
reload(_Host, _NewOpts, _OldOpts) ->
ok.
depends(_Host, _Opts) ->
[].
get_commands_spec() ->
[#ejabberd_commands{name = create_channel, tags = [group],
desc = "Create a WhatsApp like group",
module = ?MODULE, function = create_channel,
args = [{from, binary},
{channel, binary},
{service, binary}],
args_example = [<<"admin#localhost">>,
<<"testgroup123">>,
<<"mix.localhost">>],
args_desc = ["From JID", "Channel Name", "MIX Service"],
result = {res, rescode}}
].
create_channel(From, ChannelName, Service) ->
try xmpp:decode(
#xmlel{name = <<"iq">>,
attrs = [{<<"to">>, Service},
{<<"from">>, From},
{<<"type">>, <<"set">>},
{<<"id">>, p1_rand:get_string()}],
children =
[#xmlel{name = <<"create">>,
attrs = [{<<"channel">>, ChannelName},
{<<"xmlns">>, ?NS_MIX_CORE_0}]}
]},
?NS_CLIENT, []) of
#iq{type = set} = Iq ->
case mod_mix:process_mix_core(Iq) of
#iq{type = result} ->
ok;
_ ->
{error, unexpected_response}
end
catch _:{xmpp_codec, Why} ->
{error, xmpp:format_error(Why)}
end.
mod_options(_) -> [].

How do I make an individual Rocket tile asynchronous to the rest of the system

I have a multicore rocket-chip system. However I'd like one of those rocket tiles to be asynchronous from the rest.
We're trying to do that with the following:
class WithTilesCrossing extends Config((site, here, up) => {
case RocketCrossingKey => site(RocketTilesKey).head.hartId match {
case 2 => up(RocketCrossingKey) map { r =>
r.copy(crossingType = AsynchronousCrossing(),
master = TileMasterPortParams())}
case _ => up(RocketCrossingKey) map { r =>
r.copy(crossingType = SynchronousCrossing(BufferParams(1)),
master = TileMasterPortParams())}
}
})
So that is the hart with hartId = 2 should be async, the rest should be synchronous.
The above, when added to our config, doesn't appear to do anything.
However, if I use the WithAsynchronousRocketTiles from src/main/scala/subsystem/Config.scala then I get all of the tiles converted to async.
So, how would I do just a single tile?
Update based on Jack's suggestion:
Trying that code straight up gave:
[error] Config.scala:189:16: not found: value crossingType
[error] r.copy(crossingType = AsynchronousCrossing(),
[error] ^
[error] Config.scala:190:11: not found: value master
[error] master = TileMasterPortParams())
[error] ^
[error] Config.scala:192:16: not found: value crossingType
[error] r.copy(crossingType = SynchronousCrossing(BufferParams(1)),
[error] ^
[error] Config.scala:193:11: not found: value master
[error] master = TileMasterPortParams())
[error] ^
[error] four errors found
Which is surprising. So I thought I might need to do the up() thing and tried this:
case RocketCrossingKey => site(RocketTilesKey).map { r =>
r.hartId match {
case 2 => up(RocketCrossingKey) map { k => k.copy(crossingType = AsynchronousCrossing(), master = TileMasterPortParams()) }
case _ => up(RocketCrossingKey) map { k => k.copy(crossingType = SynchronousCrossing(BufferParams(1)), master = TileMasterPortParams()) }
}
}
However that results in an elab error:
[error] Caused by: java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to freechips.rocketchip.subsystem.RocketCrossingParams
[error] at freechips.rocketchip.subsystem.HasRocketTiles.$anonfun$rocketTiles$1(RocketSubsystem.scala:41)
[error] at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:234)
[error] at scala.collection.immutable.List.foreach(List.scala:389)
[error] at scala.collection.TraversableLike.map(TraversableLike.scala:234)
[error] at scala.collection.TraversableLike.map$(TraversableLike.scala:227)
[error] at scala.collection.immutable.List.map(List.scala:295)
[error] at freechips.rocketchip.subsystem.HasRocketTiles.$init$(RocketSubsystem.scala:41)
[error] at freechips.rocketchip.subsystem.RocketSubsystem.<init>(RocketSubsystem.scala:70)
So still stuck on how to modify this original RocketCrossingParams on a per-tile basis and return it.
Looking at the definition of RocketCrossingKey:
case object RocketCrossingKey extends Field[Seq[RocketCrossingParams]](List(RocketCrossingParams()))
You'll notice that the type is Seq[RocketCrossingParams]. This implies (although I may be wrong), that we have 1 RocketCrossingParams per tile. In your code snippet, you are only looking at the first of the this Seq (via .head) checking if it's hartId is equal to 2, and then if so, iterating on all of the RocketCrossingKeys and setting them to AsynchronousCrossing.
Try something like the following where we iterate on them and only replace the index for the one with hartId == 2:
case RocketCrossingKey => site(RocketTilesKey).map { r =>
if (r.hartId == 2) { // or you can match on it, whatever
r.copy(crossingType = AsynchronousCrossing(),
master = TileMasterPortParams())
} else {
r.copy(crossingType = SynchronousCrossing(BufferParams(1)),
master = TileMasterPortParams())
}
}
EDIT: I missed the fact that there is both RocketCrossingKey and RocketTilesKey
So the issue here is that there are two parallel Seqs of parameters:
RocketTilesKey which gives us RocketTileParams, 1 for each tile
RocketCrossingKey which gives us RocketCrossingParams, 1 for each tile OR if there's only 1, it applies to all
It's also possible that there is no RocketTileParams containing hartId == 2, so let's handle everything appropriately:
case RocketCrossingKey =>
val tileParams = site(RocketTilesKey)
val crossingParams = site(RocketCrossingKey)
// One might assume hartId 2 == index 2 but that may not be the case
// Also there may not even *be* a tile with hartId == 2
val indexOfHartId2: Option[Int] =
tileParams.zipWithIndex.collectFirst { case (r, idx) if r.hartId == 2 => idx }
indexOfHartId2.map { idx =>
// This duplicates logic from HasTiles.perTileOrGlobalSetting
// If there's only 1, it applies to all
val crossings = site(RocketCrossingKey) match {
case Seq(one) => List.fill(tileParams.size)(one)
case many => many
}
// Back to the original answer using the proper index for hartId == 2
crossings.zipWithIndex.map { case (c, i) =>
if (i == idx) { // or you can match on it, whatever
c.copy(crossingType = AsynchronousCrossing(),
master = TileMasterPortParams())
} else {
c.copy(crossingType = SynchronousCrossing(BufferParams(1)),
master = TileMasterPortParams())
}
}
}.getOrElse(crossingParams) // If we don't even have hardId == 2, return original value

Cannot update or delete many in ReactiveMongo 0.16

I'm having some difficulty with delete.many and update.many using the new builders whilst trying to convert my previous version's (working) code into reactivemongo 0.16.5 ("org.reactivemongo" %% "play2-reactivemongo" % "0.16.5-play26", "org.reactivemongo" %% "reactivemongo-akkastream" % "0.16.5". As you'll see; I'm using this within the Play plugin so dealing with JSON (rather than BSON)
I'm going from the official documentation here. My errors are similar for both update & delete so I'll just post for update here to keep it trim.
Update command
def updateMany(collName: String)(quJsa: JsArray)(orderedBool: Boolean = false): Future[MultiBulkWriteResult] = {
lazy val updateBuilder = getCollection(collName).map(_.update(orderedBool))
quJsa.asOpt[Seq[JsObject]].map(
_.map(
x => x.as[MongoUpdateBuilder]
)
).map(
_.map(
x => updateBuilder.flatMap(
_.element(q = x.q, u = x.getSetUpdate, upsert = x.upsertBool, multi = x.multiBool)
)
)
).map(
x => Future.sequence(x)
).map(
_.flatMap(
x => updateBuilder.flatMap(
_.many(x)
)
)
).getOrElse(getMultiBulkWriteResultErrorF("input not recognised as jsarr"))
}
Custom update builder model
case class MongoUpdateBuilder(q: JsObject, u: JsObject, upsertBool: Boolean, multiBool: Boolean) {
def getSetUpdate = Json.obj("$set" -> u)
}
object MongoUpdateBuilder {
implicit val mongoUpdateBuilderFormat = Json.format[MongoUpdateBuilder]
}
Error container
def getMultiBulkWriteResultErrorF(errStr: String): Future[MultiBulkWriteResult] = {
val mbwr = MultiBulkWriteResult(
ok = false,
n = 0,
nModified = 0,
upserted = Seq(),
writeErrors = Seq(WriteError(index = 0, code = 404, errmsg = errStr)),
writeConcernError = None,
code = Some(404),
errmsg = Some(errStr),
totalN = 0
)
Future.successful(mbwr)
}
And the main issue:
no type parameters for method flatMap: (f: reactivemongo.play.json.collection
.JSONCollection#UpdateBuilder => scala.concurrent.Future[S])(implicit executor: scala.concurrent.ExecutionContext)scala.concurrent.Future[S] exist so that it can be applied to arguments (reactivemongo.play.json.collection.JSONCollection#UpdateBuilder => scala.concurrent.Future[_1.UpdateCommand.UpdateElement] forSome { val _1: reactivemongo.play.json.collection.J
SONCollection }) [error] --- because ---
[error] argument expression's type is not compatible with formal parameter type;
[error] found : reactivemongo.play.json.collection.JSONCollection#UpdateBuilder => scala.concurrent.Future[_1.UpdateCommand.UpdateElement] forSome { val _1: reactivemongo.play.jso
n.collection.JSONCollection }
[error] required: reactivemongo.play.json.collection.JSONCollection#UpdateBuilder => scala.concurrent.Future[?S]
[error] x => updateBuilder.flatMap(
So the issue seems to be this line - updateBuilder.flatMap. The Future cannot be flattened with these types (JSONCollection#UpdateBuilder & JSONCollection#UpdateCommand.UpdateElement). So I'm struggling with this one. Please reach out if you can see the issue here. Many thanks!

java.utilNoSuchElementException: None.get for Vec

Perhaps I'm going about something the wrong way. I have a number of buffers that need to get locked and unlocked as part of the behavior of a state machine. I thought it would be perfect to use a Vec of Reg to store the state from clock to clock and use a var Vec of wires to accumulate the state as the state machine goes about locking and unlocking things. Here is code similar to the code I wrote that breaks in the same way:
import Chisel._
class testvec extends Module
{
val io = new Bundle
{
val addr = Vec( 5, UInt( INPUT, 4 ) )
val enable = Bool( INPUT )
val in = Vec( 5, UInt( INPUT, 16 ) )
val out = Vec( 16, UInt( OUTPUT, 16 ) )
}
val latch = Vec( 16, Reg( init=UInt(0,16) ) )
var temp = Vec( 16, UInt(0,16) )
for( i <- 0 until 16 )
{
temp(i) := latch(i)
}
for( i <- 0 until 5 )
{
temp(io.addr(i)) := io.in(i)
}
for( i <- 0 until 16 )
{
io.out(i) := temp(i)
}
when( io.enable )
{
for( i <- 0 until 16 )
{
latch(i) := temp(i)
}
}
}
class testvec_Tests(c: testvec) extends Tester(c)
{
step( 1 )
}
object mainStub
{
def main( args: Array[String] ): Unit =
{
chiselMainTest( Array[String]("--backend", "c", // "--backend", "v",
"--compile", "--test", "--genHarness"),
() => Module( new testvec() ) )
{
c => new testvec_Tests( c )
}
}
}
Note that although this code merely has a simple loop, I need to get my combinatorial lock state at various points during the execution of the state machine each clock cycle, so that's why this simplification has those combinatorial states as the final output rather than the registers.
Here's the full text of the error message:
[info] Set current project to chisel
[info] Running mainStub
[error] (run-main-0) java.util.NoSuchElementException: None.get
java.util.NoSuchElementException: None.get
at scala.None$.get(Option.scala:347)
at scala.None$.get(Option.scala:345)
at Chisel.ROMData$$anonfun$3.apply(ROM.scala:90)
at Chisel.ROMData$$anonfun$3.apply(ROM.scala:90)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:245)
at scala.collection.Iterator$class.foreach(Iterator.scala:750)
at scala.collection.immutable.RedBlackTree$TreeIterator.foreach(RedBlackTree.scala:468)
at scala.collection.MapLike$DefaultValuesIterable.foreach(MapLike.scala:206)
at scala.collection.TraversableLike$class.map(TraversableLike.scala:245)
at scala.collection.AbstractTraversable.map(Traversable.scala:104)
at Chisel.ROMData.<init>(ROM.scala:90)
at Chisel.ROM.data$lzycompute(ROM.scala:72)
at Chisel.ROM.data(ROM.scala:72)
at Chisel.ROM.read(ROM.scala:77)
at Chisel.Vec.apply(Vec.scala:121)
at testvec$$anonfun$2.apply$mcVI$sp(testvec.scala:21)
at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:166)
at testvec.<init>(testvec.scala:19)
at mainStub$$anonfun$main$1$$anonfun$apply$1.apply(testvec.scala:47)
at mainStub$$anonfun$main$1$$anonfun$apply$1.apply(testvec.scala:47)
at Chisel.Module$.Chisel$Module$$init(Module.scala:65)
at Chisel.Module$.apply(Module.scala:50)
at mainStub$$anonfun$main$1.apply(testvec.scala:47)
at mainStub$$anonfun$main$1.apply(testvec.scala:47)
at Chisel.Driver$.execute(Driver.scala:101)
at Chisel.Driver$.apply(Driver.scala:41)
at Chisel.Driver$.apply(Driver.scala:64)
at Chisel.chiselMain$.apply(hcl.scala:63)
at Chisel.chiselMainTest$.apply(hcl.scala:76)
at mainStub$.main(testvec.scala:48)
at mainStub.main(testvec.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
[trace] Stack trace suppressed: run last compile:run for the full output.
java.lang.RuntimeException: Nonzero exit code: 1
at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last compile:run for the full output.
[error] (compile:run) Nonzero exit code: 1
[error] Total time: 1 s, completed Mar 3, 2016 1:49:17 PM
Are you sure of your "Vec" declaration ?
According to documentation, Vec must be declared as following I think:
val io = new Bundle
{
val addr = Vec.fill(5) {UInt( INPUT, 4 )}
val enable = Bool( INPUT )
val in = Vec.fill( 5 ) {UInt( INPUT, 16 )}
val out = Vec.fill( 16 ) {UInt( OUTPUT, 16 )}
}

parsing json return from http in erlang

I test with this code :
get_fee(Transaction,SourceNumber,Amount, Currency) ->
Url = lists:concat(["http://localhost/test.php","?transaction=", Transaction, "&saccount=", SourceNumber,Amount,"&currency=",Currency]),
inets:start(),
{Flag, Response} = http:request(get, {Url, []}, [], []),
case Flag of
ok ->
{ { _, ReturnCode, _ }, _, Body } = Response,
if ReturnCode =:= 200 ->
{ok,{_,[{_,Code},{_,Permission},{_,Payer},{_,Payee}]}} = json:decode_string(Body),
case Permission of true ->
if Code =:= 200 ->
{ok,{Code, Payer, Payee}};
Code =:= 204 ->
{nok,{Code, not_found}};
true ->
{nok,{Code, parameter_error}}
end;
false ->
{nok,{Code, parameter_error}}
end;
true->
{error, http_error}
end;
error ->
case Response of
nxdomain -> {error, dns_error};
_ -> {error, network_error}
end
end.
the response from the http is : {"code":200,"permission":true,"fee_payer":0,"fee_payee":19}
But now I like to do the same think but the return of http in this case for example is :
{"CIN":"08321224","Name":21}
so I have just CIN and Name in this case
I try to change the previous
get_fee(Num) ->
Url = lists:concat(["http://localhost/GTW/Operation.php","?ACCOUNT_NUM=", Num]),
inets:start(),
{Flag, Response} = http:request(get, {Url, []}, [], []),
case Flag of
ok ->
{ { _, ReturnCode, _ }, _, Body } = Response,
%% for debug
io:format("~p~n",[ReturnCode]),
if ReturnCode =:= "08321224" ->
{ok,{_,[{_,CIN},{_,Name}]}} = json:decode_string(Body),
case Name of 21 ->
io:format(CIN),
io:format(Name),
if CIN =:= "08321224"->
{ok,{CIN, Name}};
CIN =:= 204 ->
{nok,{CIN, not_found}};
true ->
{nok,{CIN, parameter_error}}
end;
false ->
{nok,{CIN, parameter_error}}
end;
true->
{error, http_error}
end;
error ->
case Response of
nxdomain -> {error, dns_error};
_ -> {error, network_error}
%% for debug
%%io:format("pass2~n ~p~n",[Response]),
end
end.
but it displys :
test:get_fee("0001").
200
{error,http_error}
So I'll nitpick on the style here, because you will be far better off if you follow the semantical idea of Erlang:
get_fee(Num) ->
Url = lists:concat(["http://localhost/GTW/Operation.php","?ACCOUNT_NUM=", Num]),
inets:start(),
This is the wrong place to start inets. It should be started outside this function as you only need to
do this one.
{Flag, Response} = http:request(get, {Url, []}, [], []),
This part is better coded with a pattern match. The discrimination of Flag and Response can
be decoded directly with a simple match. Write,
case http:request(get, {Url, []}, [], []) of
{ok, {{_, 200, _}, _, Body}} ->
{ok, R} = json:decode_string(Body),
get_fee_decode_(get_cin(R), get_name(R));
{error, Reason} -> {error, Reason}
end.
I would recommend against changing {error, nxdomain} to {error, dns_error} since nxdomain perfectly
codes this case in any case. Just pass the error tuple to the caller and have him handle it.
get_fee_decode_("08321224" = CIN, 21 = Name) -> {ok, {CIN, Name}};
get_fee_decode_("204" = CIN, 21) -> {nok, {CIN, not_found}};
get_fee_decode_(CIN, _Name) -> {nok, {CIN, parameter_error}};
Introduce a new function like this to handle the inner parts of your code base. And hoist the matching to
top-level. This helps in the long run by decoupling your code into functions.
Do note that in a JSON structure, there is no order on an "object" so you can't assume that the structure is
{"code":200,"permission":true,"fee_payer":0,"fee_payee":19}
But a decode does not have to preserve this structure, according to JSON. So a valid decode may be:
[{"fee_payee", 19}, {"fee_payer", 0}, {"permission", true}, {"code", 200}]
This will fail to match in your code and you are setting yourself up for some nasty errors later on.
You want something along the lines of:
get_fee_payer(PL) -> proplists:get_value("fee_payer", PL).
Another thing which will be a problem with your programming style is that you are hiding error-information cases. In Erlang, you can often get away with only handling the "happy path" through the code and leave all the error handling out until you know what kind of errors are there in the code base. Then you can begin adding in error handling slowly. Defensive programming is not a thing you should be doing if you can avoid it.
You changed:
if ReturnCode =:= 200 ->
to:
if ReturnCode =:= "08321224" ->
However, that needs to stay the same in your version. 200 is the HTTP status code for "OK" - the first step here is to verify that the server actually processed the request and returned a positive reply. You'll find that number only in Body - that is what the if CIN =:= "08321224"-> part is for.