diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c
index b4c50a8ce9c632cd47f10b927b32c7312152ac73..70f120a697c06b66a4e12b3f8b1837be88811cb3 100644
--- a/targets/RT/USER/lte-softmodem.c
+++ b/targets/RT/USER/lte-softmodem.c
@@ -100,6 +100,7 @@ unsigned short config_frames[4] = {2,9,11,13};
 //////////////////////////////////
 
 #include "agent_if/read/sm_ag_if_rd.h"
+#include "agent_if/write/sm_ag_if_wr.h"
 #include "agent_if/sm_io.h"
 #include "agent_if/e2_agent_api.h"
 #include <time.h>
@@ -743,26 +744,26 @@ void read_slice_conf(slice_conf_t* conf)
       // slice algo data
       if (algo == STATIC_SLICING){
         rd_slice->params.type = SLICE_ALG_SM_V0_STATIC;
-        static_slice_t* sta = &rd_slice->params.sta;
+        static_slice_t* sta = &rd_slice->params.u.sta;
         sta->pos_high = ((static_slice_param_t *)s->algo_data)->posHigh;
         sta->pos_low = ((static_slice_param_t *)s->algo_data)->posLow;
       } else if (algo == NVS_SLICING) {
         rd_slice->params.type = SLICE_ALG_SM_V0_NVS;
-        nvs_slice_t* nvs = &rd_slice->params.nvs;
+        nvs_slice_t* nvs = &rd_slice->params.u.nvs;
         if (((nvs_slice_param_t *)s->algo_data)->type == NVS_RATE) {
           nvs->conf = SLICE_SM_NVS_V0_RATE;
           const float rsvd = ((nvs_slice_param_t *)s->algo_data)->Mbps_reserved;
           const float ref = ((nvs_slice_param_t *)s->algo_data)->Mbps_reference;
-          nvs->rate.mbps_required = rsvd;
-          nvs->rate.mbps_reference = ref;
+          nvs->u.rate.u1.mbps_required = rsvd;
+          nvs->u.rate.u2.mbps_reference = ref;
         } else {
           const float rsvd = ((nvs_slice_param_t *)s->algo_data)->pct_reserved;
           nvs->conf = SLICE_SM_NVS_V0_CAPACITY;
-          nvs->capacity.pct_reserved = rsvd;
+          nvs->u.capacity.u.pct_reserved = rsvd;
         }
       } else if (algo == EDF_SLICING) {
         rd_slice->params.type = SLICE_ALG_SM_V0_EDF;
-        edf_slice_t* edf = &rd_slice->params.edf;
+        edf_slice_t* edf = &rd_slice->params.u.edf;
         edf->deadline = ((edf_slice_param_t *)s->algo_data)->deadline;
         edf->guaranteed_prbs = ((edf_slice_param_t *)s->algo_data)->guaranteed_prbs;
         edf->max_replenish = ((edf_slice_param_t *)s->algo_data)->max_replenish;
@@ -838,6 +839,278 @@ void read_slice_sm(slice_ind_msg_t* data)
   read_ue_slice_conf(&data->ue_slice_conf);
 }
 
+static
+void set_new_dl_slice_algo(slice_algorithm_e algo)
+{
+  eNB_MAC_INST *mac = RC.mac[mod_id];
+  assert(mac);
+
+  pp_impl_param_t dl = mac->pre_processor_dl;
+  switch (algo) {
+    case SLICE_ALG_SM_V0_STATIC:
+      mac->pre_processor_dl = static_dl_init(mod_id, CC_id);
+      break;
+    case SLICE_ALG_SM_V0_NVS:
+      mac->pre_processor_dl = nvs_dl_init(mod_id, CC_id);
+      break;
+    case SLICE_ALG_SM_V0_EDF:
+      mac->pre_processor_dl = edf_dl_init(mod_id, CC_id);
+      break;
+    default:
+      mac->pre_processor_dl.algorithm = 0;
+      mac->pre_processor_dl.dl = dlsch_scheduler_pre_processor;
+      mac->pre_processor_dl.dl_algo.data = mac->pre_processor_dl.dl_algo.setup();
+      mac->pre_processor_dl.slices = NULL;
+      break;
+  }
+  if (dl.slices)
+    dl.destroy(&dl.slices);
+  if (dl.dl_algo.data)
+    dl.dl_algo.unset(&dl.dl_algo.data);
+}
+
+static
+int add_mod_dl_slice(slice_algorithm_e current_algo, fr_slice_t const* slice)
+{
+  void *params = NULL;
+  char *slice_algo = NULL;
+  if (current_algo == SLICE_ALG_SM_V0_STATIC) {
+    assert(current_algo == SLICE_ALG_SM_V0_STATIC);
+    slice_algo = strdup("STATIC");
+    // TODO: this should be copied inside addmod_slice() to avoid unnecessary
+    // copies, but reuse the old code for the moment
+    params = malloc(sizeof(static_slice_param_t));
+    if (!params) return -1;
+    ((static_slice_param_t *)params)->posLow = slice->params.u.sta.pos_low;
+    ((static_slice_param_t *)params)->posHigh = slice->params.u.sta.pos_high;
+  } else if (current_algo == SLICE_ALG_SM_V0_NVS) {
+    assert(current_algo == SLICE_ALG_SM_V0_NVS);
+    params = malloc(sizeof(nvs_slice_param_t));
+    if (!params) return -1;
+    if (slice->params.u.nvs.conf == SLICE_SM_NVS_V0_RATE) {
+      slice_algo = strdup("NVS_RATE");
+      ((nvs_slice_param_t *)params)->type = NVS_RATE;
+      ((nvs_slice_param_t *)params)->Mbps_reserved = slice->params.u.nvs.u.rate.u1.mbps_required;
+      ((nvs_slice_param_t *)params)->Mbps_reference = slice->params.u.nvs.u.rate.u2.mbps_reference;
+    } else {
+      assert(slice->params.u.nvs.conf == SLICE_SM_NVS_V0_CAPACITY);
+      slice_algo = strdup("NVS_CAPACITY");
+      ((nvs_slice_param_t *)params)->type = NVS_RES;
+      ((nvs_slice_param_t *)params)->pct_reserved = slice->params.u.nvs.u.capacity.u.pct_reserved;
+    }
+  } else if (current_algo == SLICE_ALG_SM_V0_EDF) {
+    assert(current_algo == SLICE_ALG_SM_V0_EDF);
+    slice_algo = strdup("EDF");
+    params = malloc(sizeof(edf_slice_param_t));
+    if (!params) return -1;
+    ((edf_slice_param_t *)params)->deadline = slice->params.u.edf.deadline;
+    ((edf_slice_param_t *)params)->guaranteed_prbs = slice->params.u.edf.guaranteed_prbs;
+    ((edf_slice_param_t *)params)->max_replenish = slice->params.u.edf.max_replenish;
+    ((edf_slice_param_t *)params)->noverride = slice->params.u.edf.len_over;
+    for (int i = 0; i < ((edf_slice_param_t *)params)->noverride; ++i)
+      ((edf_slice_param_t *)params)->loverride[i] = slice->params.u.edf.over[i];
+  } else {
+    assert(0 != 0 && "Unknow current_algo");
+  }
+
+  pp_impl_param_t *dl = &RC.mac[mod_id]->pre_processor_dl;
+  void *algo = &dl->dl_algo;
+  char *ue_algo = NULL;
+  if (slice->sched) {
+    if (!strcmp(slice->sched, "RR"))
+      ue_algo = strdup("round_robin_dl");
+    else if (!strcmp(slice->sched, "PF"))
+      ue_algo = strdup("proportional_fair_wbcqi_dl");
+    else if (!strcmp(slice->sched, "MT"))
+      ue_algo = strdup("maximum_throughput_wbcqi_dl");
+    else
+      LOG_E(MAC, "unknow scheduler '%s'\n", slice->sched);
+
+    algo = dlsym(NULL, ue_algo);
+
+    if (!algo) {
+      free(params);
+      LOG_E(MAC, "cannot locate scheduler '%s'\n", slice->sched);
+      return -15;
+    }
+  }
+
+  char *l = NULL;
+  if (slice->label)
+    l = strdup(slice->label);
+  uint8_t sid = slice->id;
+  LOG_W(MAC, "add DL slice id %d, label %s, slice sched algo %s, ue sched algo %s\n", sid, l, slice_algo, ue_algo);
+  return dl->addmod_slice(dl->slices, sid, l, algo, params);
+}
+
+static
+int find_dl_slice(uint32_t id)
+{
+  slice_info_t *si = RC.mac[mod_id]->pre_processor_dl.slices;
+  for (int i = 0; i < si->num; ++i)
+    if (si->s[i]->id == id)
+      return i;
+  return -1;
+}
+
+static
+int del_dl_slice(uint32_t id)
+{
+  const int idx = find_dl_slice(id);
+  if (idx < 0)
+    return -1;
+  pp_impl_param_t *dl = &RC.mac[mod_id]->pre_processor_dl;
+  return dl->remove_slice(dl->slices, idx);
+}
+
+static
+int get_ue_id(uint16_t rnti)
+{
+  const UE_info_t* ue_info = &RC.mac[mod_id]->UE_info;
+  const UE_list_t* ue_list = &ue_info->list;
+  for (int ue_id = ue_list->head; ue_id >= 0; ue_id = ue_list->next[ue_id]) {
+    if (ue_info->UE_template[CC_id][ue_id].rnti == rnti)
+      return ue_id;
+  }
+  return -1;
+}
+
+static
+int assoc_ue_to_dl_slice(uint16_t rnti, uint32_t id)
+{
+  int idx = find_dl_slice(id);
+  if (idx < 0)
+    return -100;
+  int ue_id = get_ue_id(rnti);
+  if (ue_id < 0)
+    return -101;
+
+  pp_impl_param_t *dl = &RC.mac[mod_id]->pre_processor_dl;
+  LOG_W(MAC, "associate UE RNTI 0x%04x from slice ID %d idx %d to slice ID %d idx %d\n",
+        rnti, dl->slices->UE_assoc_slice[ue_id], find_dl_slice(dl->slices->UE_assoc_slice[ue_id]), id, idx);
+  dl->move_UE(dl->slices, ue_id, idx);
+  return 0;
+}
+
+static
+sm_ag_if_ans_e write_slice_sm(slice_ctrl_msg_t const* data)
+{
+  assert(data != NULL);
+
+  /// ADD MOD ///
+  if (data->type == SLICE_CTRL_SM_V0_ADD) {
+    eNB_MAC_INST *mac = RC.mac[mod_id];
+    assert(mac);
+    /// GET DL SLICE CONTROL INFO ///
+    slice_conf_t const* wr = &data->u.add_mod_slice;
+    size_t slices_len = wr->dl.len_slices;
+
+    if (slices_len >= 0) {
+      int current_algo = mac->pre_processor_dl.algorithm;
+      int new_algo = -1;
+      if (slices_len > 0)
+        new_algo = wr->dl.slices[0].params.type;
+      pthread_mutex_lock(&mac->pp_dl_mutex);
+      if (current_algo != new_algo) {
+        set_new_dl_slice_algo(new_algo);
+        current_algo = new_algo;
+        if (new_algo > 0)
+          LOG_D(NR_MAC, "set new algorithm %d\n", current_algo);
+        else
+          LOG_W(NR_MAC, "reset slicing algorithm as NONE\n");
+      }
+
+      for (size_t i = 0; i < slices_len; ++i) {
+        const int rc = add_mod_dl_slice(current_algo, &wr->dl.slices[i]);
+        if (rc < 0) {
+          pthread_mutex_unlock(&mac->pp_dl_mutex);
+          LOG_E(MAC, "error code %d while updating slices\n", rc);
+          return SLICE_CTRL_OUT_ERROR;
+        }
+      }
+      pthread_mutex_unlock(&mac->pp_dl_mutex);
+      return SLICE_CTRL_OUT_OK;
+    } else {
+      assert(0 != 0 && "slices_len <= 0");
+      return SLICE_CTRL_OUT_ERROR;
+    }
+  } else if (data->type == SLICE_CTRL_SM_V0_DEL) {
+    /// DEL ///
+    eNB_MAC_INST *mac = RC.mac[mod_id];
+    assert(mac);
+    if (mac->pre_processor_dl.algorithm <= 0) {
+      LOG_E(MAC, "current slice algo is NONE, no slice can be deleted\n");
+      return SLICE_CTRL_OUT_ERROR;
+    }
+    /// GET DL SLICE CONTROL INFO ///
+    del_slice_conf_t const* wr = &data->u.del_slice;
+    uint32_t* dl_ids = wr->dl;
+    size_t n_dl_ids = wr->len_dl;
+    assert(n_dl_ids != 0 && "del_slice->len_dl == 0");
+
+    pthread_mutex_lock(&mac->pp_dl_mutex);
+    for (size_t i = 0; i < n_dl_ids; ++i) {
+      LOG_W(MAC, "attempt to delete slice ID %d\n", dl_ids[i]);
+      const int rc = del_dl_slice(dl_ids[i]);
+      if (rc < 0) {
+        pthread_mutex_unlock(&mac->pp_dl_mutex);
+        LOG_E(MAC, "error code %d while deleting slice ID %d\n", rc, dl_ids[i]);
+        return SLICE_CTRL_OUT_ERROR;
+      }
+    }
+    pthread_mutex_unlock(&mac->pp_dl_mutex);
+    return SLICE_CTRL_OUT_OK;
+  } else if (data->type == SLICE_CTRL_SM_V0_UE_SLICE_ASSOC) {
+    /// ASSOC SLICE ///
+    eNB_MAC_INST *mac = RC.mac[mod_id];
+    assert(mac);
+    if (mac->pre_processor_dl.algorithm <= 0) {
+      LOG_E(NR_MAC, "current slice algo is NONE, no UE can be associated\n");
+      return SLICE_CTRL_OUT_ERROR;
+    }
+    if (mac->pre_processor_dl.slices->num <= 0) {
+      LOG_E(NR_MAC, "no UE connected\n");
+      return SLICE_CTRL_OUT_ERROR;
+    }
+    /// GET DL SLICE CONTROL INFO ///
+    ue_slice_conf_t const* wr = &data->u.ue_slice;
+    ue_slice_assoc_t* new_ues = wr->ues;
+    size_t n_new_ues = wr->len_ue_slice;
+    assert(n_new_ues != 0 && "ue_slice->len_ue_slice == 0");
+
+    pthread_mutex_lock(&mac->pp_dl_mutex);
+    for (size_t i = 0; i < n_new_ues; ++i) {
+      // uint16_t rnti = new_ues[i].rnti;
+      uint16_t rnti = mac->UE_info.eNB_UE_stats[CC_id][0].crnti; // current FlexRIC can not pass the rnti info by ctrl req
+      if (new_ues[i].ul_id)
+        LOG_W(MAC, "ignoring UL slice association for RNTI %04x\n", rnti);
+      uint32_t new_idx = new_ues[i].dl_id;
+      int ue_id = get_ue_id(rnti);
+      if (ue_id < 0) {
+        pthread_mutex_unlock(&mac->pp_dl_mutex);
+        LOG_E(MAC, "no ue connected, ue_id %d\n", ue_id);
+        return SLICE_CTRL_OUT_ERROR;
+      }
+      int cur_idx = slicing_get_UE_slice_idx(mac->pre_processor_dl.slices, ue_id);
+      if (new_idx == cur_idx) {
+        pthread_mutex_unlock(&mac->pp_dl_mutex);
+        LOG_E(MAC, "expected DL slice association for UE RNTI %04x\n", rnti);
+        return SLICE_CTRL_OUT_ERROR;
+      }
+      int rc = assoc_ue_to_dl_slice(rnti, new_idx);
+      if (rc < 0) {
+        pthread_mutex_unlock(&mac->pp_dl_mutex);
+        LOG_E(MAC, "error code %d while associating RNTI %04x\n", rc, rnti);
+        return SLICE_CTRL_OUT_ERROR;
+      }
+    }
+    pthread_mutex_unlock(&mac->pp_dl_mutex);
+    return SLICE_CTRL_OUT_OK;
+  } else {
+    assert(0 != 0 && "Unknow slice ctrl type");
+  }
+}
+
 static
 void read_RAN(sm_ag_if_rd_t* data)
 {
@@ -869,9 +1142,15 @@ static
 sm_ag_if_ans_t write_RAN(sm_ag_if_wr_t const* data)
 {
   assert(data != NULL);
-  assert(0!=0 && "Not implemented");
-  sm_ag_if_ans_t ans = {.type = MAC_AGENT_IF_CTRL_ANS_V0 };
+  if(data->type == SLICE_CTRL_REQ_V0){
+    sm_ag_if_ans_t ans = {.type = SLICE_AGENT_IF_CTRL_ANS_V0};
+    ans.slice.ans = write_slice_sm(&data->slice_req_ctrl.msg);
+    return ans;
+  } else {
+    assert(0 != 0 && "Not supported function ");
+  }
 
+  sm_ag_if_ans_t ans = {0};
   return ans;
 }